Skip to content

Commit

Permalink
implemented get and initial search
Browse files Browse the repository at this point in the history
  • Loading branch information
herzrasen committed Dec 30, 2022
1 parent ed53a3c commit 7b4045c
Show file tree
Hide file tree
Showing 14 changed files with 336 additions and 59 deletions.
11 changes: 10 additions & 1 deletion args/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@ type RecordCmd struct {
Command string `arg:"positional"`
}

type SearchCmd struct {
}

type ListCmd struct {
NoCount bool `arg:"--no-count"`
NoLastUpdate bool `arg:"--no-last-update"`
WithId bool `arg:"--with-id"`
Limit int `arg:"-l,--limit" default:"-1"`
}

type GetCmd struct {
Index int64 `arg:"--index"`
}

type DeleteCmd struct {
Ids []int64 `arg:"-i,--id"`
Filter string `arg:"-f,--filter"`
Pattern string `arg:"-p,--pattern" help:"Delete all records matching the pattern"`
MaxCount int64 `arg:"--max-count" help:"Delete all records with a count of at most max-count"`
}

type Args struct {
Record *RecordCmd `arg:"subcommand:record"`
Search *SearchCmd `arg:"subcommand:search"`
Get *GetCmd `arg:"subcommand:get"`
List *ListCmd `arg:"subcommand:list"`
Delete *DeleteCmd `arg:"subcommand:delete"`
Config string `arg:"--config" default:"~/.config/hist/config.yml"`
Expand Down
15 changes: 0 additions & 15 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/jmoiron/sqlx"
"github.com/mattn/go-sqlite3"
_ "github.com/mattn/go-sqlite3"
log "github.com/sirupsen/logrus"
"regexp"
)

Expand Down Expand Up @@ -36,19 +35,5 @@ func NewSqliteClient(path string) (*Client, error) {
if err != nil {
return nil, fmt.Errorf("client.NewClient: create table: %w", err)
}
rows, err := db.Query(`SELECT command FROM hist WHERE regexp("^make.*", command) = true`)
if err != nil {
log.WithError(err).Fatal("Unable to select stuff")
}
defer rows.Close()
for rows.Next() {
var command string
err := rows.Scan(&command)
if err != nil {
log.WithError(err).Warn("Unable to scan row")
} else {
fmt.Printf("'%s'\n", command)
}
}
return &Client{Path: path, Db: db}, nil
}
21 changes: 12 additions & 9 deletions client/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,19 @@ import (
)

const (
stmtDeleteByIds = `DELETE FROM hist WHERE id IN (?)`
stmtDeleteByFilter = `DELETE FROM hist WHERE command LIKE ?`
stmtDeleteByIds = `DELETE FROM hist WHERE id IN (?)`
)

type DeleteOptions struct {
Ids []int64
Filter string
Ids []int64
Pattern string
}

func (c *Client) Delete(options DeleteOptions) error {
if len(options.Ids) > 0 {
return c.deleteByIds(options)
} else if options.Filter != "" {
return c.deleteByFilter(options)
} else if options.Pattern != "" {
return c.deleteByPattern(options)
}
return nil
}
Expand All @@ -36,13 +35,17 @@ func (c *Client) deleteByIds(options DeleteOptions) error {
return nil
}

func (c *Client) deleteByFilter(options DeleteOptions) error {
prefix := fmt.Sprintf("%s%%", options.Filter)
res, err := c.Db.Exec(stmtDeleteByFilter, prefix)
func (c *Client) deleteByPattern(options DeleteOptions) error {
stmt := buildDeleteByPatternStatement(options.Pattern)
res, err := c.Db.Exec(stmt)
if err != nil {
return fmt.Errorf("hist.Client.Delete: exec prefix: %w", err)
}
x, err := res.RowsAffected()
fmt.Printf("Deleted %d entries\n", x)
return nil
}

func buildDeleteByPatternStatement(pattern string) string {
return fmt.Sprintf("DELETE FROM hist WHERE regexp('%s', command) = true", pattern)
}
24 changes: 12 additions & 12 deletions client/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,23 @@ import (

func TestClient_Delete(t *testing.T) {
t.Run("delete by filter", func(t *testing.T) {
db, mock, _ := sqlmock.New()
mock.ExpectExec("DELETE FROM hist WHERE command LIKE ?").
WithArgs("test-command%").
db, mock, _ := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
mock.ExpectExec("DELETE FROM hist WHERE regexp('^test-command.*', command) = true").
WillReturnResult(sqlmock.NewResult(0, 2))
c := Client{Db: sqlx.NewDb(db, "sqlite3")}
err := c.Delete(DeleteOptions{Filter: "test-command"})
err := c.Delete(DeleteOptions{Pattern: "^test-command.*"})
require.NoError(t, err)
})

t.Run("exec returns err with filter", func(t *testing.T) {
db, mock, _ := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
mock.ExpectExec("DELETE FROM hist WHERE regexp('test', command) = true").
WillReturnError(errors.New("some error"))
c := Client{Db: sqlx.NewDb(db, "sqlite3")}
err := c.Delete(DeleteOptions{Pattern: "test"})
require.Error(t, err)
})

t.Run("delete by ids", func(t *testing.T) {
db, mock, _ := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
mock.ExpectExec("DELETE FROM hist WHERE id IN (?, ?)").
Expand All @@ -38,12 +46,4 @@ func TestClient_Delete(t *testing.T) {
require.Error(t, err)
})

t.Run("exec returns err with filter", func(t *testing.T) {
db, mock, _ := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
mock.ExpectExec("DELETE FROM hist WHERE command LIKE ?").
WillReturnError(errors.New("some error"))
c := Client{Db: sqlx.NewDb(db, "sqlite3")}
err := c.Delete(DeleteOptions{Filter: "test"})
require.Error(t, err)
})
}
26 changes: 26 additions & 0 deletions client/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package client

import (
"fmt"
)

func (c *Client) Get(index int64) (string, error) {
rows, err := c.Db.Query(`SELECT command
FROM hist
ORDER BY last_update DESC
LIMIT 1
OFFSET ?`, index)
if err != nil {
return "", fmt.Errorf("client:Client:Get: query: %w", err)
}
defer rows.Close()
for rows.Next() {
var command string
err := rows.Scan(&command)
if err != nil {
return "", fmt.Errorf("client:Client:Get: scan row: %w", err)
}
return command, nil
}
return "", fmt.Errorf("unable to get record with index: %d", index)
}
34 changes: 34 additions & 0 deletions client/get_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package client

import (
"errors"
"github.com/DATA-DOG/go-sqlmock"
"github.com/jmoiron/sqlx"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)

func TestClient_Get(t *testing.T) {
t.Run("succeed", func(t *testing.T) {
db, mock, _ := sqlmock.New()
mock.ExpectQuery("SELECT command FROM hist ORDER BY last_update DESC LIMIT 1 OFFSET ?").
WithArgs(101).
WillReturnRows(sqlmock.NewRows([]string{"command"}).
AddRow("my-command --help"))
c := Client{Db: sqlx.NewDb(db, "sqlite3")}
command, err := c.Get(101)
require.NoError(t, err)
assert.Equal(t, "my-command --help", command)
})

t.Run("query fails", func(t *testing.T) {
db, mock, _ := sqlmock.New()
mock.ExpectQuery("SELECT command FROM hist ORDER BY last_update DESC LIMIT 1 OFFSET ?").
WithArgs(101).
WillReturnError(errors.New("some error"))
c := Client{Db: sqlx.NewDb(db, "sqlite3")}
_, err := c.Get(101)
require.Error(t, err)
})
}
17 changes: 11 additions & 6 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import (
"fmt"
"gopkg.in/yaml.v3"
"os"
"regexp"
"strings"
)

type Config struct {
type Patterns struct {
Excludes []string `yaml:"excludes"`
}
type Config struct {
Patterns Patterns `yaml:"patterns"`
}

func Load(path string) (*Config, error) {
path, err := resolvePath(path)
Expand Down Expand Up @@ -39,11 +43,12 @@ func resolvePath(path string) (string, error) {
}

func (c *Config) IsExcluded(s string) bool {
if c.Excludes == nil {
return false
}
for _, exclude := range c.Excludes {
if s == exclude {
for _, pattern := range c.Patterns.Excludes {
matches, err := regexp.MatchString(pattern, s)
if err != nil {
continue
}
if matches {
return true
}
}
Expand Down
34 changes: 32 additions & 2 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,43 @@ func TestLoad(t *testing.T) {
f, err := os.CreateTemp("", "hist-config-*")
require.NoError(t, err)
_, err = f.WriteString(`---
excludes:
patterns:
excludes:
- go run
- ll
`)
require.NoError(t, err)
c, err := Load(f.Name())
require.NoError(t, err)
assert.Len(t, c.Excludes, 2)
assert.Len(t, c.Patterns.Excludes, 2)
})
}

func TestConfig_IsExcluded(t *testing.T) {
t.Run("is excluded", func(t *testing.T) {
c := Config{
Patterns: Patterns{Excludes: []string{
"^foo.*",
}},
}
assert.True(t, c.IsExcluded("foo bar"))
})

t.Run("is excluded II", func(t *testing.T) {
c := Config{
Patterns: Patterns{Excludes: []string{
"foo bar",
}},
}
assert.True(t, c.IsExcluded("this foo bar is excluded"))
})

t.Run("not excluded", func(t *testing.T) {
c := Config{
Patterns: Patterns{Excludes: []string{
"^foo.*",
}},
}
assert.False(t, c.IsExcluded("bar baz"))
})
}
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ require (
github.com/adrg/xdg v0.4.0
github.com/alexflint/go-arg v1.4.3
github.com/fatih/color v1.13.0
github.com/gdamore/tcell/v2 v2.5.3
github.com/jmoiron/sqlx v1.3.5
github.com/mattn/go-sqlite3 v1.14.16
github.com/rivo/tview v0.0.0-20221221172820-02e38ea9604c
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.7.0
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
Expand All @@ -17,9 +19,15 @@ require (
github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect
github.com/alexflint/go-scalar v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
github.com/stretchr/objx v0.1.0 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/text v0.3.7 // indirect
)
22 changes: 22 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,35 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0=
github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/tview v0.0.0-20221221172820-02e38ea9604c h1:Y4GSXEYKYAtguH10lmQmYb7hRkJ7U+m8GvnFHKU2jrk=
github.com/rivo/tview v0.0.0-20221221172820-02e38ea9604c/go.mod h1:lBUy/T5kyMudFzWUH/C2moN+NlU5qF505vzOyINXuUQ=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
Expand All @@ -36,10 +49,19 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
Expand Down
Loading

0 comments on commit 7b4045c

Please sign in to comment.