commit 89014e8581ecabbd6cd7ff338f39c4cefbd572d4
parent 75c0b1a8cb647c469040679e4a83650514feabf7
Author: Sean Enck <sean@ttypty.com>
Date: Wed, 4 Jun 2025 23:20:56 -0400
all logic for regexp filters can go into QueryCallback
Diffstat:
6 files changed, 38 insertions(+), 61 deletions(-)
diff --git a/internal/app/conv.go b/internal/app/conv.go
@@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
- "regexp"
"strings"
"git.sr.ht/~enckse/lockbox/internal/backend"
@@ -32,31 +31,18 @@ func Conv(cmd CommandOptions) error {
}
func serialize(w io.Writer, tx *backend.Transaction, isJSON bool, filter string) error {
- e, err := tx.QueryCallback(backend.QueryOptions{Mode: backend.ListMode, Values: backend.JSONValue})
+ e, err := tx.QueryCallback(backend.QueryOptions{Mode: backend.ListMode, Values: backend.JSONValue, PathFilter: filter})
if err != nil {
return err
}
if isJSON {
fmt.Fprint(w, "{")
}
- hasFilter := len(filter) > 0
- var re *regexp.Regexp
- if hasFilter {
- re, err = regexp.Compile(filter)
- if err != nil {
- return err
- }
- }
printed := false
for item, err := range e {
if err != nil {
return err
}
- if hasFilter {
- if !re.MatchString(item.Path) {
- continue
- }
- }
if printed {
if isJSON {
fmt.Fprint(w, ",")
diff --git a/internal/app/core.go b/internal/app/core.go
@@ -5,7 +5,6 @@ import (
"fmt"
"io"
"os"
- "regexp"
"git.sr.ht/~enckse/lockbox/internal/backend"
"git.sr.ht/~enckse/lockbox/internal/platform"
@@ -97,22 +96,3 @@ func (a DefaultCommand) Password() (string, error) {
func (a *DefaultCommand) Input(interactive bool) ([]byte, error) {
return platform.GetUserInputPassword(interactive)
}
-
-func generatePrinter[T any](w io.Writer, isFilter bool, filter string, toMatch, toDisplay func(T) string) (func(T), error) {
- printer := func(p T) {
- fmt.Fprintf(w, "%s\n", toDisplay(p))
- }
- finder := printer
- if isFilter {
- re, err := regexp.Compile(filter)
- if err != nil {
- return nil, err
- }
- finder = func(p T) {
- if re.MatchString(toMatch(p)) {
- printer(p)
- }
- }
- }
- return finder, nil
-}
diff --git a/internal/app/list.go b/internal/app/list.go
@@ -3,6 +3,7 @@ package app
import (
"errors"
+ "fmt"
"git.sr.ht/~enckse/lockbox/internal/backend"
)
@@ -16,6 +17,7 @@ func List(cmd CommandOptions, isFind bool) error {
if len(args) != 1 {
return errors.New("find requires one argument")
}
+ opts.PathFilter = args[0]
} else {
if len(args) != 0 {
return errors.New("list does not support any arguments")
@@ -26,23 +28,11 @@ func List(cmd CommandOptions, isFind bool) error {
return err
}
w := cmd.Writer()
- filter := ""
- if isFind {
- filter = args[0]
- }
- finder, err := generatePrinter(w, isFind, filter, func(p string) string {
- return p
- }, func(p string) string {
- return p
- })
- if err != nil {
- return err
- }
for f, err := range e {
if err != nil {
return err
}
- finder(f.Path)
+ fmt.Fprintf(w, "%s\n", f.Path)
}
return nil
}
diff --git a/internal/app/totp.go b/internal/app/totp.go
@@ -225,24 +225,16 @@ func (args *TOTPArguments) Do(opts TOTPOptions) error {
return ErrNoTOTP
}
if args.Mode == ListTOTPMode || args.Mode == FindTOTPMode {
- e, err := opts.app.Transaction().QueryCallback(backend.QueryOptions{Mode: backend.SuffixMode, Criteria: backend.NewSuffix(args.token)})
+ e, err := opts.app.Transaction().QueryCallback(backend.QueryOptions{Mode: backend.SuffixMode, Criteria: backend.NewSuffix(args.token), PathFilter: args.Entry})
if err != nil {
return err
}
writer := opts.app.Writer()
- filter, err := generatePrinter(writer, args.Mode == FindTOTPMode, args.Entry, func(e backend.Entity) string {
- return e.Path
- }, func(e backend.Entity) string {
- return e.Directory()
- })
- if err != nil {
- return err
- }
for entry, err := range e {
if err != nil {
return err
}
- filter(entry)
+ fmt.Fprintf(writer, "%s\n", entry.Directory())
}
return nil
}
diff --git a/internal/backend/query.go b/internal/backend/query.go
@@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "regexp"
"slices"
"strings"
@@ -17,9 +18,10 @@ import (
type (
// QueryOptions indicates how to find entities
QueryOptions struct {
- Criteria string
- Mode QueryMode
- Values ValueMode
+ PathFilter string
+ Criteria string
+ Mode QueryMode
+ Values ValueMode
}
// JSON is an entry as a JSON string
JSON struct {
@@ -129,6 +131,15 @@ func (t *Transaction) QueryCallback(args QueryOptions) (QuerySeq2, error) {
var entities []entity
isSort := args.Mode != ExactMode
decrypt := args.Values != BlankValue
+ hasPathFilter := args.PathFilter != ""
+ var pathFilter *regexp.Regexp
+ if hasPathFilter {
+ var err error
+ pathFilter, err = regexp.Compile(args.PathFilter)
+ if err != nil {
+ return nil, err
+ }
+ }
err := t.act(func(ctx Context) error {
forEach("", ctx.db.Content.Root.Groups[0].Groups, ctx.db.Content.Root.Groups[0].Entries, func(offset string, entry gokeepasslib.Entry) {
path := getPathName(entry)
@@ -157,6 +168,11 @@ func (t *Transaction) QueryCallback(args QueryOptions) (QuerySeq2, error) {
}
}
}
+ if hasPathFilter {
+ if !pathFilter.MatchString(path) {
+ return
+ }
+ }
obj := entity{backing: entry, path: path}
if isSort && len(entities) > 0 {
i, _ := slices.BinarySearchFunc(entities, obj, func(i, j entity) int {
diff --git a/internal/backend/query_test.go b/internal/backend/query_test.go
@@ -202,6 +202,19 @@ func TestQueryCallback(t *testing.T) {
if res[0].Path != "test/test/abc" {
t.Errorf("invalid results: %v", res)
}
+ seq, err = fullSetup(t, true).QueryCallback(backend.QueryOptions{Mode: backend.ExactMode, Criteria: "test/test/abc", PathFilter: "abc"})
+ if err != nil {
+ t.Errorf("no error: %v", err)
+ }
+ res = testCollect(t, 1, seq)
+ if res[0].Path != "test/test/abc" {
+ t.Errorf("invalid results: %v", res)
+ }
+ seq, err = fullSetup(t, true).QueryCallback(backend.QueryOptions{Mode: backend.ExactMode, Criteria: "test/test/abc", PathFilter: "abz"})
+ if err != nil {
+ t.Errorf("no error: %v", err)
+ }
+ testCollect(t, 0, seq)
seq, err = fullSetup(t, true).QueryCallback(backend.QueryOptions{Mode: backend.ExactMode, Criteria: "abczzz"})
if err != nil {
t.Errorf("no error: %v", err)