refactor: lint fixes with golangci-lint

This commit is contained in:
Dan Anglin 2021-09-15 10:09:48 +01:00
parent 7d97ed4834
commit 839467c557
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
6 changed files with 134 additions and 34 deletions

35
.golangci.yaml Normal file
View file

@ -0,0 +1,35 @@
---
run:
concurrency: 2
timeout: 1m
issues-exit-code: 1
tests: true
output:
format: colored-line-number
print-issues-lines: true
print-linter-name: true
uniq-by-line: true
sort-results: true
linters-settings:
exhaustivestruct:
struct-patterns:
- 'forge.dananglin.me.uk/code/dananglin/pelican.Status'
- 'forge.dananglin.me.uk/code/dananglin/pelican.Card'
lll:
line-length: 140
testpackage:
skip-regexp: (internal)_test\.go
linters:
enable-all: true
disable:
- gomnd
fast: false
issues:
exclude-rules:
- path: db_internal_test.go
linters:
- funlen

69
db.go
View file

@ -19,7 +19,11 @@ const (
) )
func mkDataDir(dir string) error { func mkDataDir(dir string) error {
return os.MkdirAll(dir, 0700) if err := os.MkdirAll(dir, 0o700); err != nil {
return fmt.Errorf("error while making directory %s, %w", dir, err)
}
return nil
} }
// dbPath returns the path to the database file. If a path is given then that is returned. Otherwise the default path is returned. // dbPath returns the path to the database file. If a path is given then that is returned. Otherwise the default path is returned.
@ -32,6 +36,7 @@ func mkDataDir(dir string) error {
func dbPath(path string) (string, error) { func dbPath(path string) (string, error) {
if len(path) > 0 { if len(path) > 0 {
filepath.Dir(path) filepath.Dir(path)
return path, mkDataDir(filepath.Dir(path)) return path, mkDataDir(filepath.Dir(path))
} }
@ -53,14 +58,13 @@ func dbPath(path string) (string, error) {
dataDir = filepath.Join(os.Getenv("HOME"), ".pelican") dataDir = filepath.Join(os.Getenv("HOME"), ".pelican")
} }
if err := os.MkdirAll(dataDir, 0700); err != nil { if err := os.MkdirAll(dataDir, 0o700); err != nil {
return "", fmt.Errorf("unable to make directory %s, %w", dataDir, err) return "", fmt.Errorf("unable to make directory %s, %w", dataDir, err)
} }
path = filepath.Join(dataDir, dbFilename) path = filepath.Join(dataDir, dbFilename)
return path, mkDataDir(dataDir) return path, mkDataDir(dataDir)
} }
// openDatabase opens the database, at a given path, for reading and writing. If the file does not exist it will be created. // openDatabase opens the database, at a given path, for reading and writing. If the file does not exist it will be created.
@ -69,7 +73,7 @@ func openDatabase(path string) (*bolt.DB, error) {
Timeout: 1 * time.Second, Timeout: 1 * time.Second,
} }
db, err := bolt.Open(path, 0600, &opts) db, err := bolt.Open(path, 0o600, &opts)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to open database at %s, %w", path, err) return nil, fmt.Errorf("unable to open database at %s, %w", path, err)
} }
@ -81,14 +85,20 @@ func openDatabase(path string) (*bolt.DB, error) {
func ensureBuckets(db *bolt.DB) error { func ensureBuckets(db *bolt.DB) error {
buckets := []string{statusBucket, cardBucket} buckets := []string{statusBucket, cardBucket}
return db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
for _, v := range buckets { for _, v := range buckets {
if _, err := tx.CreateBucketIfNotExists([]byte(v)); err != nil { if _, err := tx.CreateBucketIfNotExists([]byte(v)); err != nil {
return fmt.Errorf("unable to ensure that %s bucket is created in the database, %w", v, err) return fmt.Errorf("unable to ensure that %s bucket is created in the database, %w", v, err)
} }
} }
return nil return nil
}) })
if err != nil {
return fmt.Errorf("error while ensuring buckets exist in the database, %w", err)
}
return nil
} }
// saveStatuses saves one or more statuses to the status bucket. // saveStatuses saves one or more statuses to the status bucket.
@ -99,22 +109,22 @@ func saveStatuses(db *bolt.DB, statuses []Status) error {
bucket := []byte(statusBucket) bucket := []byte(statusBucket)
return db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket) b := tx.Bucket(bucket)
if b == nil { if b == nil {
return fmt.Errorf("bucket %s does not exist", bucket) return bucketNotExistError{bucket: string(bucket)}
} }
for _, v := range statuses { for _, v := range statuses {
var err error var err error
if v.Id == 0 { if v.ID < 1 {
var id uint64 var id uint64
if id, err = b.NextSequence(); err != nil { if id, err = b.NextSequence(); err != nil {
return fmt.Errorf("unable to generate ID, %w", err) return fmt.Errorf("unable to generate ID, %w", err)
} }
v.Id = int(id) v.ID = int(id)
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@ -123,25 +133,31 @@ func saveStatuses(db *bolt.DB, statuses []Status) error {
return fmt.Errorf("unable to encode data, %w", err) return fmt.Errorf("unable to encode data, %w", err)
} }
if err = b.Put([]byte(strconv.Itoa(v.Id)), buf.Bytes()); err != nil { if err = b.Put([]byte(strconv.Itoa(v.ID)), buf.Bytes()); err != nil {
return fmt.Errorf("unable to add the status to the bucket, %w", err) return fmt.Errorf("unable to add the status to the bucket, %w", err)
} }
} }
return nil return nil
}) })
if err != nil {
return fmt.Errorf("error while saving the statuses to the database, %w", err)
}
return nil
} }
// loadAllStatuses retrieves all the statuses from the status bucket. // loadAllStatuses retrieves all the statuses from the status bucket.
func loadAllStatuses(db *bolt.DB) ([]Status, error) { func loadAllStatuses(db *bolt.DB) ([]Status, error) {
var statuses []Status var statuses []Status
bucket := []byte(statusBucket) bucket := []byte(statusBucket)
err := db.View(func(tx *bolt.Tx) error { err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket) b := tx.Bucket(bucket)
if b == nil { if b == nil {
return fmt.Errorf("bucket %s does not exist", bucket) return bucketNotExistError{bucket: string(bucket)}
} }
if err := b.ForEach(func(_, v []byte) error { if err := b.ForEach(func(_, v []byte) error {
@ -156,12 +172,16 @@ func loadAllStatuses(db *bolt.DB) ([]Status, error) {
return nil return nil
}); err != nil { }); err != nil {
return err return fmt.Errorf("unable to load status, %w", err)
} }
return nil return nil
}) })
if err != nil {
return statuses, fmt.Errorf("error while loading statuses from the database, %w", err)
}
return statuses, err return statuses, nil
} }
// saveCard writes a card to the card bucket in BoltDB. // saveCard writes a card to the card bucket in BoltDB.
@ -173,15 +193,15 @@ func saveCard(db *bolt.DB, card Card) (int, error) {
b := tx.Bucket(bucket) b := tx.Bucket(bucket)
if b == nil { if b == nil {
return fmt.Errorf("bucket %s does not exist", bucket) return bucketNotExistError{bucket: string(bucket)}
} }
if card.Id == 0 { if card.ID < 1 {
var id uint64 var id uint64
if id, err = b.NextSequence(); err != nil { if id, err = b.NextSequence(); err != nil {
return fmt.Errorf("unable to generate an ID for the card, %w", err) return fmt.Errorf("unable to generate an ID for the card, %w", err)
} }
card.Id = int(id) card.ID = int(id)
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@ -190,26 +210,30 @@ func saveCard(db *bolt.DB, card Card) (int, error) {
return fmt.Errorf("unable to encode data, %w", err) return fmt.Errorf("unable to encode data, %w", err)
} }
if err = b.Put([]byte(strconv.Itoa(card.Id)), buf.Bytes()); err != nil { if err = b.Put([]byte(strconv.Itoa(card.ID)), buf.Bytes()); err != nil {
return fmt.Errorf("unable to write the card to the bucket, %w", err) return fmt.Errorf("unable to write the card to the bucket, %w", err)
} }
return nil return nil
}) })
if err != nil {
return card.ID, fmt.Errorf("error while saving the card to the database, %w", err)
}
return card.Id, err return card.ID, nil
} }
// loadCard retrieves a card from the cards bucket in BoltDB. // loadCard retrieves a card from the cards bucket in BoltDB.
func loadCard(db *bolt.DB, id int) (Card, error) { func loadCard(db *bolt.DB, id int) (Card, error) {
var card Card var card Card
bucket := []byte(cardBucket) bucket := []byte(cardBucket)
err := db.View(func(tx *bolt.Tx) error { err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket) b := tx.Bucket(bucket)
if b == nil { if b == nil {
return fmt.Errorf("bucket %s does not exist", bucket) return bucketNotExistError{bucket: string(bucket)}
} }
buf := bytes.NewBuffer(b.Get([]byte(strconv.Itoa(id)))) buf := bytes.NewBuffer(b.Get([]byte(strconv.Itoa(id))))
@ -221,6 +245,9 @@ func loadCard(db *bolt.DB, id int) (Card, error) {
return nil return nil
}) })
if err != nil {
return card, err return card, fmt.Errorf("error while loading the card from the database, %w", err)
}
return card, nil
} }

View file

@ -12,6 +12,8 @@ import (
) )
func TestDbCustomPath(t *testing.T) { func TestDbCustomPath(t *testing.T) {
t.Parallel()
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst getting the current directory, %s", err) t.Fatalf("An error occurred whilst getting the current directory, %s", err)
@ -35,6 +37,8 @@ func TestDbCustomPath(t *testing.T) {
} }
func TestDbDefaultPath(t *testing.T) { func TestDbDefaultPath(t *testing.T) {
t.Parallel()
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst getting the current directory, %s", err) t.Fatalf("An error occurred whilst getting the current directory, %s", err)
@ -70,7 +74,10 @@ func TestDbDefaultPath(t *testing.T) {
} }
func TestEnsureBuckets(t *testing.T) { func TestEnsureBuckets(t *testing.T) {
t.Parallel()
var db *bolt.DB var db *bolt.DB
var err error var err error
testDB := "test/databases/TestEnsureBuckets.db" testDB := "test/databases/TestEnsureBuckets.db"
@ -93,9 +100,10 @@ func TestEnsureBuckets(t *testing.T) {
if err = db.View(func(tx *bolt.Tx) error { if err = db.View(func(tx *bolt.Tx) error {
for _, b := range expectedBuckets { for _, b := range expectedBuckets {
if bucket := tx.Bucket([]byte(b)); bucket == nil { if bucket := tx.Bucket([]byte(b)); bucket == nil {
return fmt.Errorf("bucket %s does not exist in the database", b) return bucketNotExistError{bucket: b}
} }
} }
return nil return nil
}); err != nil { }); err != nil {
t.Fatalf("An error occurred whilst checking for the buckets, %s.", err) t.Fatalf("An error occurred whilst checking for the buckets, %s.", err)
@ -103,7 +111,10 @@ func TestEnsureBuckets(t *testing.T) {
} }
func TestReadAndWriteStatuses(t *testing.T) { func TestReadAndWriteStatuses(t *testing.T) {
t.Parallel()
var db *bolt.DB var db *bolt.DB
var err error var err error
testDB := "test/databases/TestWriteAndReadStatuses.db" testDB := "test/databases/TestWriteAndReadStatuses.db"
@ -122,18 +133,22 @@ func TestReadAndWriteStatuses(t *testing.T) {
// Begin with an initial list of statuses // Begin with an initial list of statuses
initialStatusList := []Status{ initialStatusList := []Status{
{ {
ID: -1,
Name: "Backlog", Name: "Backlog",
CardIds: []int{1, 14, 9, 10}, CardIds: []int{1, 14, 9, 10},
}, },
{ {
ID: -1,
Name: "Next", Name: "Next",
CardIds: []int{2, 5, 12}, CardIds: []int{2, 5, 12},
}, },
{ {
ID: -1,
Name: "In progress", Name: "In progress",
CardIds: []int{3}, CardIds: []int{3},
}, },
{ {
ID: -1,
Name: "Finished!", Name: "Finished!",
CardIds: []int{4, 6, 7, 8, 11, 13}, CardIds: []int{4, 6, 7, 8, 11, 13},
}, },
@ -146,6 +161,7 @@ func TestReadAndWriteStatuses(t *testing.T) {
// load the status list from the database, modify it a little and write it to the database. // load the status list from the database, modify it a little and write it to the database.
var modifiedStatusList []Status var modifiedStatusList []Status
if modifiedStatusList, err = loadAllStatuses(db); err != nil { if modifiedStatusList, err = loadAllStatuses(db); err != nil {
t.Fatalf("An error occurred whilst reading the initial status list to the database, %s", err) t.Fatalf("An error occurred whilst reading the initial status list to the database, %s", err)
} }
@ -153,6 +169,7 @@ func TestReadAndWriteStatuses(t *testing.T) {
modifiedStatusList[2].CardIds = []int{3, 14} modifiedStatusList[2].CardIds = []int{3, 14}
archiveStatus := Status{ archiveStatus := Status{
ID: -1,
Name: "Archived", Name: "Archived",
CardIds: []int{34, 51, 894}, CardIds: []int{34, 51, 894},
} }
@ -166,33 +183,34 @@ func TestReadAndWriteStatuses(t *testing.T) {
// read the final status list from the database and compare to the expected status list. // read the final status list from the database and compare to the expected status list.
want := []Status{ want := []Status{
{ {
Id: 1, ID: 1,
Name: "Backlog", Name: "Backlog",
CardIds: []int{1, 14, 9, 10}, CardIds: []int{1, 14, 9, 10},
}, },
{ {
Id: 2, ID: 2,
Name: "Next", Name: "Next",
CardIds: []int{2, 5, 12}, CardIds: []int{2, 5, 12},
}, },
{ {
Id: 3, ID: 3,
Name: "In progress", Name: "In progress",
CardIds: []int{3, 14}, CardIds: []int{3, 14},
}, },
{ {
Id: 4, ID: 4,
Name: "Finished!", Name: "Finished!",
CardIds: []int{4, 6, 7, 8, 11, 13}, CardIds: []int{4, 6, 7, 8, 11, 13},
}, },
{ {
Id: 5, ID: 5,
Name: "Archived", Name: "Archived",
CardIds: []int{34, 51, 894}, CardIds: []int{34, 51, 894},
}, },
} }
var got []Status var got []Status
if got, err = loadAllStatuses(db); err != nil { if got, err = loadAllStatuses(db); err != nil {
t.Fatalf("An error occurred whilst reading the modified status list from the database, %s", err) t.Fatalf("An error occurred whilst reading the modified status list from the database, %s", err)
} }
@ -205,7 +223,10 @@ func TestReadAndWriteStatuses(t *testing.T) {
} }
func TestReadAndWriteCards(t *testing.T) { func TestReadAndWriteCards(t *testing.T) {
t.Parallel()
var db *bolt.DB var db *bolt.DB
var err error var err error
testDB := "test/databases/TestReadWriteCards.db" testDB := "test/databases/TestReadWriteCards.db"
@ -220,35 +241,36 @@ func TestReadAndWriteCards(t *testing.T) {
} }
initialCard := Card{ initialCard := Card{
ID: -1,
Title: "A test task.", Title: "A test task.",
Content: "This task should be completed.", Content: "This task should be completed.",
} }
// Save the card // Save the card
cardId, err := saveCard(db, initialCard) cardID, err := saveCard(db, initialCard)
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst saving the initial card to the database, %s", err) t.Fatalf("An error occurred whilst saving the initial card to the database, %s", err)
} }
modifiedCard, err := loadCard(db, cardId) modifiedCard, err := loadCard(db, cardID)
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst loading the card from the database, %s", err) t.Fatalf("An error occurred whilst loading the card from the database, %s", err)
} }
modifiedCard.Title = "Task: Foo bar baz." modifiedCard.Title = "Task: Foo bar baz."
modifiedCardId, err := saveCard(db, modifiedCard) modifiedCardID, err := saveCard(db, modifiedCard)
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst saving the modified card to the database, %s", err) t.Fatalf("An error occurred whilst saving the modified card to the database, %s", err)
} }
want := Card{ want := Card{
Id: 1, ID: 1,
Title: "Task: Foo bar baz.", Title: "Task: Foo bar baz.",
Content: "This task should be completed.", Content: "This task should be completed.",
} }
got, err := loadCard(db, modifiedCardId) got, err := loadCard(db, modifiedCardID)
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst loading the modified from the database, %s", err) t.Fatalf("An error occurred whilst loading the modified from the database, %s", err)
} }

11
errors.go Normal file
View file

@ -0,0 +1,11 @@
package main
import "fmt"
type bucketNotExistError struct {
bucket string
}
func (e bucketNotExistError) Error() string {
return fmt.Sprintf("bucket %s does not exist", e.bucket)
}

View file

@ -2,14 +2,14 @@ package main
// Status represents the status of the Kanban board. // Status represents the status of the Kanban board.
type Status struct { type Status struct {
Id int ID int
Name string Name string
CardIds []int CardIds []int
} }
// Card represents a card on a Kanban board // Card represents a card on a Kanban board.
type Card struct { type Card struct {
Id int ID int
Title string Title string
Content string Content string
} }

View file

@ -35,6 +35,11 @@ func Test() error {
return goTest(args...) return goTest(args...)
} }
// Lint runs golangci-lint against the code.
func Lint() error {
return sh.RunV("golangci-lint", "run", "--color", "always")
}
// Build build the executable // Build build the executable
func Build() error { func Build() error {
return sh.Run("go", "build", "-o", binary, "main.go") return sh.Run("go", "build", "-o", binary, "main.go")