lockbox

password manager
Log | Files | Refs | README | LICENSE

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:
Minternal/app/conv.go | 16+---------------
Minternal/app/core.go | 20--------------------
Minternal/app/list.go | 16+++-------------
Minternal/app/totp.go | 12++----------
Minternal/backend/query.go | 22+++++++++++++++++++---
Minternal/backend/query_test.go | 13+++++++++++++
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)