diff --git a/internal/board/board.go b/internal/board/board.go index d1f6415..a0545ae 100644 --- a/internal/board/board.go +++ b/internal/board/board.go @@ -10,16 +10,25 @@ import ( bolt "go.etcd.io/bbolt" ) -// OpenProject reads the project from the database. If no board exists then a new one will be created. -func OpenProject(path string) (*bolt.DB, error) { +type Board struct { + db *bolt.DB +} + +// Open reads the board from the database. +// If no board exists then a new one will be created. +func Open(path string) (Board, error) { db, err := database.OpenDatabase(path) if err != nil { - return nil, fmt.Errorf("unable to open the database, %w", err) + return Board{}, fmt.Errorf("unable to open the database, %w", err) } - statusList, err := ReadStatusList(db) + board := Board{ + db: db, + } + + statusList, err := board.StatusList() if err != nil { - return nil, err + return Board{}, err } if len(statusList) == 0 { @@ -32,16 +41,21 @@ func OpenProject(path string) (*bolt.DB, error) { } if _, err := database.WriteMany(db, database.StatusBucket, boltItems); err != nil { - return nil, fmt.Errorf("unable to save the default status list to the database, %w", err) + return Board{}, fmt.Errorf("unable to save the default status list to the database, %w", err) } } - return db, nil + + return board, nil } -// ReadStatusList returns the ordered list of statuses from the database. -func ReadStatusList(db *bolt.DB) ([]Status, error) { - data, err := database.ReadAll(db, database.StatusBucket) +func (b *Board) Close() error { + return b.db.Close() +} + +// StatusList returns the ordered list of statuses from the database. +func (b *Board) StatusList() ([]Status, error) { + data, err := database.ReadAll(b.db, database.StatusBucket) if err != nil { return []Status{}, fmt.Errorf("unable to read the status list, %w", err) } @@ -68,22 +82,22 @@ func ReadStatusList(db *bolt.DB) ([]Status, error) { } // TODO: Finish implementation. -func ReadStatus(db *bolt.DB) (Status, error) { +func (b *Board) ReadStatus() (Status, error) { return Status{}, nil } // TODO: Finish implementation. -func CreateStatus(db *bolt.DB) error { +func (b *Board) NewStatus() error { return nil } // TODO: Finish implementation. -func UpdateStatus(db *bolt.DB) error { +func (b *Board) UpdateStatus() error { return nil } // TODO: Finish implementation. -func DeleteStatus(db *bolt.DB) error { +func (b *Board) DeleteStatus() error { return nil } @@ -93,8 +107,8 @@ type CardArgs struct { } // CreateCard creates a card in the database. -func CreateCard(db *bolt.DB, args CardArgs) error { - statusList, err := ReadStatusList(db) +func (b *Board) CreateCard(args CardArgs) error { + statusList, err := b.StatusList() if err != nil { return fmt.Errorf("unable to read the status list, %w", err) } @@ -109,7 +123,7 @@ func CreateCard(db *bolt.DB, args CardArgs) error { Content: args.NewContent, } - cardID, err := database.Write(db, database.CardBucket, &card) + cardID, err := database.Write(b.db, database.CardBucket, &card) if err != nil { return fmt.Errorf("unable to write card to the database, %w", err) } @@ -118,16 +132,16 @@ func CreateCard(db *bolt.DB, args CardArgs) error { initialStatus.AddCardID(cardID) - if _, err := database.Write(db, database.StatusBucket, &initialStatus); err != nil { + if _, err := database.Write(b.db, database.StatusBucket, &initialStatus); err != nil { return fmt.Errorf("unable to write the %s status to the database, %w", initialStatus.Name, err) } return nil } -// ReadCard returns a Card value from the database. -func ReadCard(db *bolt.DB, id int) (Card, error) { - data, err := database.Read(db, database.CardBucket, id) +// Card returns a Card value from the database. +func (b *Board) Card(id int) (Card, error) { + data, err := database.Read(b.db, database.CardBucket, id) if err != nil { return Card{}, fmt.Errorf("unable to read card [%d] from the database, %w", id, err) } @@ -145,10 +159,10 @@ func ReadCard(db *bolt.DB, id int) (Card, error) { return card, nil } -// ReadCardList returns a list of Card values from the database. +// CardList returns a list of Card values from the database. // TODO: function needs testing. -func ReadCardList(db *bolt.DB, ids []int) ([]Card, error) { - data, err := database.ReadMany(db, database.CardBucket, ids) +func (b *Board) CardList(ids []int) ([]Card, error) { + data, err := database.ReadMany(b.db, database.CardBucket, ids) if err != nil { return nil, fmt.Errorf("unable to read card list from the database, %w", err) } @@ -178,8 +192,8 @@ type UpdateCardArgs struct { } // UpdateCard modifies an existing card in the database. -func UpdateCard(db *bolt.DB, args UpdateCardArgs) error { - card, err := ReadCard(db, args.CardID) +func (b *Board) UpdateCard(args UpdateCardArgs) error { + card, err := b.Card(args.CardID) if err != nil { return err } @@ -192,7 +206,7 @@ func UpdateCard(db *bolt.DB, args UpdateCardArgs) error { card.Content = args.NewContent } - if _, err := database.Write(db, database.CardBucket, &card); err != nil { + if _, err := database.Write(b.db, database.CardBucket, &card); err != nil { return fmt.Errorf("unable to write card to the database, %w", err) } @@ -207,12 +221,12 @@ type UpdateCardStatusArgs struct { // UpdateCardStatus moves a card between statuses. // TODO: finish implementation. -func UpdateCardStatus(db *bolt.DB, args UpdateCardStatusArgs) error { +func (b *Board) UpdateCardStatus(args UpdateCardStatusArgs) error { return nil } // DeleteCard deletes a card from the database. // TODO: finish implementation. -func DeleteCard(db *bolt.DB, id int) error { +func (b *Board) DeleteCard(id int) error { return nil } diff --git a/internal/board/board_test.go b/internal/board/board_test.go index 7c494c8..b0c705c 100644 --- a/internal/board/board_test.go +++ b/internal/board/board_test.go @@ -8,7 +8,6 @@ import ( "testing" "codeflow.dananglin.me.uk/apollo/canal/internal/board" - bolt "go.etcd.io/bbolt" ) func TestCardLifecycle(t *testing.T) { @@ -22,33 +21,33 @@ func TestCardLifecycle(t *testing.T) { testDBPath := filepath.Join(projectDir, "test", "databases", "Board_TestCardLifecycle.db") os.Remove(testDBPath) - db, err := board.OpenProject(testDBPath) + b, err := board.Open(testDBPath) if err != nil { t.Fatalf("Unable to open the test database %s, %s.", testDBPath, err) } defer func() { - _ = db.Close() + _ = b.Close() }() initialCardTitle := "A test card." initialCardContent := "Ensure that this card is safely stored in the database." expectedCardID := 1 - testCreateCard(t, db, initialCardTitle, initialCardContent, expectedCardID) - testReadCard(t, db, expectedCardID, initialCardTitle, initialCardContent) + testCreateCard(t, b, initialCardTitle, initialCardContent, expectedCardID) + testReadCard(t, b, expectedCardID, initialCardTitle, initialCardContent) modifiedCardTitle := "Test card updated." modifiedCardContent1 := "Ensure that this card is safely updated in the database." - testUpdateCard(t, db, expectedCardID, modifiedCardTitle, modifiedCardContent1) + testUpdateCard(t, b, expectedCardID, modifiedCardTitle, modifiedCardContent1) modifiedCardContent2 := "Updated card content only." - testUpdateCardContent(t, db, expectedCardID, modifiedCardTitle, modifiedCardContent2) + testUpdateCardContent(t, b, expectedCardID, modifiedCardTitle, modifiedCardContent2) } -func testCreateCard(t *testing.T, db *bolt.DB, title, content string, wantID int) { +func testCreateCard(t *testing.T, b board.Board, title, content string, wantID int) { t.Helper() args := board.CardArgs{ @@ -56,11 +55,11 @@ func testCreateCard(t *testing.T, db *bolt.DB, title, content string, wantID int NewContent: content, } - if err := board.CreateCard(db, args); err != nil { + if err := b.CreateCard(args); err != nil { t.Fatalf("Unable to create the test card, %s.", err) } - statusList, err := board.ReadStatusList(db) + statusList, err := b.StatusList() if err != nil { t.Fatalf("Unable to run `ReadStatusList`, %s.", err) } @@ -82,10 +81,10 @@ func testCreateCard(t *testing.T, db *bolt.DB, title, content string, wantID int } } -func testReadCard(t *testing.T, db *bolt.DB, cardID int, wantTitle, wantContent string) { +func testReadCard(t *testing.T, b board.Board, cardID int, wantTitle, wantContent string) { t.Helper() - card, err := board.ReadCard(db, cardID) + card, err := b.Card(cardID) if err != nil { t.Fatalf("Unable to read test card, %s.", err) } @@ -103,7 +102,7 @@ func testReadCard(t *testing.T, db *bolt.DB, cardID int, wantTitle, wantContent } } -func testUpdateCard(t *testing.T, db *bolt.DB, cardID int, newTitle, newContent string) { +func testUpdateCard(t *testing.T, b board.Board, cardID int, newTitle, newContent string) { t.Helper() args := board.UpdateCardArgs{ @@ -114,11 +113,11 @@ func testUpdateCard(t *testing.T, db *bolt.DB, cardID int, newTitle, newContent }, } - if err := board.UpdateCard(db, args); err != nil { + if err := b.UpdateCard(args); err != nil { t.Fatalf("Unable to update the test card, %s", err) } - got, err := board.ReadCard(db, cardID) + got, err := b.Card(cardID) if err != nil { t.Fatalf("Unable to read the modified test card, %s", err) } @@ -136,7 +135,7 @@ func testUpdateCard(t *testing.T, db *bolt.DB, cardID int, newTitle, newContent } } -func testUpdateCardContent(t *testing.T, db *bolt.DB, cardID int, expectedTitle, newContent string) { +func testUpdateCardContent(t *testing.T, b board.Board, cardID int, expectedTitle, newContent string) { t.Helper() args := board.UpdateCardArgs{ @@ -147,11 +146,11 @@ func testUpdateCardContent(t *testing.T, db *bolt.DB, cardID int, expectedTitle, }, } - if err := board.UpdateCard(db, args); err != nil { + if err := b.UpdateCard(args); err != nil { t.Fatalf("Unable to update the test card, %s", err) } - got, err := board.ReadCard(db, cardID) + got, err := b.Card(cardID) if err != nil { t.Fatalf("Unable to read the modified test card, %s", err) } diff --git a/internal/ui/app.go b/internal/ui/app.go index aba1b9b..e8f6adb 100644 --- a/internal/ui/app.go +++ b/internal/ui/app.go @@ -5,7 +5,6 @@ import ( "codeflow.dananglin.me.uk/apollo/canal/internal/board" "github.com/rivo/tview" - bolt "go.etcd.io/bbolt" ) type shiftDirection int @@ -29,34 +28,28 @@ type App struct { flex *tview.Flex pages *tview.Pages focusedColumn int - db *bolt.DB + board board.Board } // shutdown shuts down the application. func (a *App) shutdown() { - a.closeDB() + a.closeBoard() a.Stop() } -// closeDB closes the BoltDB database. -func (a *App) closeDB() { - if a.db != nil { - _ = a.db.Close() - } +// closeBoard closes the BoltDB database. +func (a *App) closeBoard() { + _ = a.board.Close() } -// openProject opens the kanban project. -func (a *App) openProject(path string) error { - if a.db != nil && len(a.db.Path()) > 0 { - a.db.Close() - } - - db, err := board.OpenProject(path) +// openBoard opens the kanban project. +func (a *App) openBoard(path string) error { + b, err := board.Open(path) if err != nil { return fmt.Errorf("unable to load board, %w", err) } - a.db = db + a.board = b if err = a.refresh(); err != nil { return err @@ -67,7 +60,7 @@ func (a *App) openProject(path string) error { // refresh refreshes the UI. func (a *App) refresh() error { - statusList, err := board.ReadStatusList(a.db) + statusList, err := a.board.StatusList() if err != nil { return fmt.Errorf("unable to get the status list, %w", err) } @@ -88,7 +81,7 @@ func (a *App) updateBoard(statusList []board.Status) error { columns[i] = a.newColumn(statusList[i].ID, statusList[i].Name) if len(statusList[i].CardIds) > 0 { - cards, err := board.ReadCardList(a.db, statusList[i].CardIds) + cards, err := a.board.CardList(statusList[i].CardIds) if err != nil { return fmt.Errorf("unable to get the card list. %w", err) } @@ -136,7 +129,7 @@ func (a *App) newCard(title, content string) error { NewContent: content, } - if err := board.CreateCard(a.db, args); err != nil { + if err := a.board.CreateCard(args); err != nil { return fmt.Errorf("unable to create card, %w", err) } diff --git a/internal/ui/init.go b/internal/ui/init.go index fce54af..f521a47 100644 --- a/internal/ui/init.go +++ b/internal/ui/init.go @@ -34,7 +34,7 @@ func initApp(a *App) { a.pages.ShowPage(quitPage) a.SetFocus(quit) } else if event.Rune() == 'o' { - a.openProject("") + a.openBoard("") } else if event.Rune() == 'a' { a.pages.ShowPage(addPage) a.SetFocus(add)