db: return error from RenderForNav, contextAction.Callback

This commit is contained in:
mappu 2024-07-13 18:03:42 +12:00
parent ee3110162b
commit e1a9f187cb
9 changed files with 52 additions and 40 deletions

View File

@ -33,7 +33,7 @@ func (ld *badgerLoadedDatabase) Keepalive(ndata *navData) {
ld.arena = append(ld.arena, ndata)
}
func (ld *badgerLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
func (ld *badgerLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) error {
// Load properties
@ -74,13 +74,13 @@ func (ld *badgerLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
return nil
})
if err != nil {
vcl.ShowMessage(fmt.Sprintf("Failed to load data: %s", err.Error()))
return
return err
}
// Valid
vcl_stringgrid_columnwidths(f.contentBox)
f.contentBox.SetEnabled(true)
return nil
}
func (n *badgerLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {

View File

@ -39,7 +39,7 @@ func (ld *boltLoadedDatabase) Keepalive(ndata *navData) {
ld.arena = append(ld.arena, ndata)
}
func (ld *boltLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
func (ld *boltLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) error {
// Load properties
@ -72,13 +72,13 @@ func (ld *boltLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
return nil
})
if err != nil {
vcl.ShowMessage(fmt.Sprintf("Failed to load data for bucket %q: %s", bucketDisplayName, err.Error()))
return
return err
}
// Valid
vcl_stringgrid_columnwidths(f.contentBox)
f.contentBox.SetEnabled(true)
return nil
}
func (n *boltLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {
@ -143,13 +143,14 @@ func (ld *boltLoadedDatabase) NavContext(ndata *navData) (ret []contextAction, e
if len(ndata.bucketPath) > 0 {
ret = append(ret, contextAction{"Delete bucket", ld.DeleteBucket})
}
return
}
func (ld *boltLoadedDatabase) AddChildBucket(ndata *navData) {
func (ld *boltLoadedDatabase) AddChildBucket(ndata *navData) error {
bucketName := ""
if !vcl.InputQuery(APPNAME, "Enter a name for the new bucket:", &bucketName) {
return // cancel
return nil // cancel
}
err := ld.db.Update(func(tx *bbolt.Tx) error {
@ -164,11 +165,12 @@ func (ld *boltLoadedDatabase) AddChildBucket(ndata *navData) {
return err
})
if err != nil {
vcl.ShowMessageFmt("Error adding bucket: %v", err)
return fmt.Errorf("Error adding bucket: %w", err)
}
return nil
}
func (ld *boltLoadedDatabase) DeleteBucket(ndata *navData) {
func (ld *boltLoadedDatabase) DeleteBucket(ndata *navData) error {
err := ld.db.Update(func(tx *bbolt.Tx) error {
// Find parent of this bucket.
if len(ndata.bucketPath) >= 2 {
@ -181,8 +183,9 @@ func (ld *boltLoadedDatabase) DeleteBucket(ndata *navData) {
}
})
if err != nil {
vcl.ShowMessageFmt("Error deleting bucket %q: %v", strings.Join(ndata.bucketPath, `/`), err)
return fmt.Errorf("Error deleting bucket %q: %w", strings.Join(ndata.bucketPath, `/`), err)
}
return nil
}
func (ld *boltLoadedDatabase) ExecQuery(query string, resultArea *vcl.TStringGrid) error {

View File

@ -35,7 +35,7 @@ func (ld *debconfLoadedDatabase) Keepalive(ndata *navData) {
ld.arena = append(ld.arena, ndata)
}
func (ld *debconfLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
func (ld *debconfLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) error {
// Load properties
@ -67,6 +67,7 @@ func (ld *debconfLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
// Valid
vcl_stringgrid_columnwidths(f.contentBox)
f.contentBox.SetEnabled(true)
return nil
}
func (n *debconfLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {

View File

@ -18,8 +18,9 @@ func (n *noLoadedDatabase) RootElement() *vcl.TTreeNode {
return nil
}
func (n *noLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
func (n *noLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) error {
f.propertiesBox.SetText("Open a database to get started...")
return nil
}
func (n *noLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {

View File

@ -35,7 +35,7 @@ func (ld *pebbleLoadedDatabase) Keepalive(ndata *navData) {
ld.arena = append(ld.arena, ndata)
}
func (ld *pebbleLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
func (ld *pebbleLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) error {
ctx := context.Background()
@ -59,8 +59,7 @@ func (ld *pebbleLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
k := itr.Key()
v, err := itr.ValueAndErr()
if err != nil {
vcl.ShowMessage(fmt.Sprintf("Failed to load data for key %q: %s", formatAny(k), err.Error()))
return
return fmt.Errorf("Failed to load data for key %q: %w", formatAny(k), err)
}
rpos := f.contentBox.RowCount()
@ -72,6 +71,7 @@ func (ld *pebbleLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
// Valid
vcl_stringgrid_columnwidths(f.contentBox)
f.contentBox.SetEnabled(true)
return nil
}
func (n *pebbleLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {

View File

@ -40,34 +40,32 @@ func (ld *redisLoadedDatabase) Keepalive(ndata *navData) {
ld.arena = append(ld.arena, ndata)
}
func (ld *redisLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
func (ld *redisLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) error {
ctx := context.Background()
if len(ndata.bucketPath) == 0 {
// Top-level: Show info() on main Properties tab
infostr, err := ld.db.Info(ctx).Result()
if err != nil {
vcl.ShowMessage(fmt.Sprintf("Retreiving database info: %v", err))
return
return fmt.Errorf("Retreiving database info: %w", err)
}
f.propertiesBox.SetText(infostr)
// Leave data tab disabled (default behaviour)
return nil
} else if len(ndata.bucketPath) == 1 {
// One selected database
// Figure out its content
err := ld.db.Do(ctx, "SELECT", ndata.bucketPath[0]).Err()
if err != nil {
vcl.ShowMessage(fmt.Sprintf("Switching to database %q: %v", ndata.bucketPath[0], err))
return
return fmt.Errorf("Switching to database %q: %w", ndata.bucketPath[0], err)
}
allKeys, err := ld.db.Keys(ctx, "*").Result()
if err != nil {
vcl.ShowMessage(fmt.Sprintf("Listing keys in database %q: %v", ndata.bucketPath[0], err))
return
return fmt.Errorf("Listing keys in database %q: %w", ndata.bucketPath[0], err)
}
f.propertiesBox.SetText(fmt.Sprintf("Database %s\nTotal keys: %d\n", ndata.bucketPath[0], len(allKeys)))
@ -84,8 +82,7 @@ func (ld *redisLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
for _, key := range allKeys {
typeName, err := ld.db.Type(ctx, key).Result()
if err != nil {
vcl.ShowMessage(fmt.Sprintf("Loading %q/%q: %v", ndata.bucketPath[0], key, err))
return
return fmt.Errorf("Loading %q/%q: %w", ndata.bucketPath[0], key, err)
}
rpos := f.contentBox.RowCount()
@ -97,16 +94,14 @@ func (ld *redisLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
case "string":
val, err := ld.db.Get(ctx, key).Result()
if err != nil {
vcl.ShowMessage(fmt.Sprintf("Loading %q/%q: %v", ndata.bucketPath[0], key, err))
return
return fmt.Errorf("Loading %q/%q: %w", ndata.bucketPath[0], key, err)
}
f.contentBox.SetCells(2, rpos, val)
case "hash":
val, err := ld.db.HGetAll(ctx, key).Result()
if err != nil {
vcl.ShowMessage(fmt.Sprintf("Loading %q/%q: %v", ndata.bucketPath[0], key, err))
return
return fmt.Errorf("Loading %q/%q: %w", ndata.bucketPath[0], key, err)
}
// It's a map[string]string
f.contentBox.SetCells(2, rpos, formatAny(val))
@ -129,10 +124,10 @@ func (ld *redisLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
// Valid
vcl_stringgrid_columnwidths(f.contentBox)
f.contentBox.SetEnabled(true)
return nil
} else {
vcl.ShowMessage(fmt.Sprintf("Unexpected nav position %q", ndata.bucketPath))
return
return fmt.Errorf("Unexpected nav position %q", ndata.bucketPath)
}
}

View File

@ -2,6 +2,7 @@ package main
import (
"database/sql"
"errors"
"fmt"
"path/filepath"
"unsafe"
@ -36,15 +37,17 @@ func (ld *sqliteLoadedDatabase) Keepalive(ndata *navData) {
ld.arena = append(ld.arena, ndata)
}
func (ld *sqliteLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
func (ld *sqliteLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) error {
if len(ndata.bucketPath) == 0 {
// Top-level
f.propertiesBox.SetText("Please select...")
return nil
} else if len(ndata.bucketPath) == 1 {
// Category (tables, ...)
f.propertiesBox.SetText("Please select...")
return nil
} else if len(ndata.bucketPath) == 2 && ndata.bucketPath[0] == sqliteTablesCaption {
// Render for specific table
@ -67,8 +70,7 @@ func (ld *sqliteLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
// work even when there are 0 results
columnNames, err := ld.sqliteGetColumnNamesForTable(tableName)
if err != nil {
vcl.ShowMessageFmt("Failed to load columns for table %q: %s", tableName, err.Error())
return
return fmt.Errorf("Failed to load columns for table %q: %w", tableName)
}
populateColumns(columnNames, f.contentBox)
@ -79,8 +81,7 @@ func (ld *sqliteLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
// Select * with small limit
datar, err := ld.db.Query(`SELECT * FROM "` + tableName + `" LIMIT 1000`) // WARNING can't prepare this parameter, but it comes from the DB (trusted)
if err != nil {
vcl.ShowMessageFmt("Failed to load data for table %q: %s", tableName, err.Error())
return
return fmt.Errorf("Failed to load data for table %q: %w", tableName, err)
}
defer datar.Close()
populateRows(datar, f.contentBox)
@ -88,9 +89,11 @@ func (ld *sqliteLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
// We successfully populated the data grid
vcl_stringgrid_columnwidths(f.contentBox)
f.contentBox.SetEnabled(true)
return nil
} else {
// ??? unknown
return errors.New("?")
}

View File

@ -11,7 +11,7 @@ var ErrNotSupported error = errors.New("Unsupported action for this database typ
type contextAction struct {
Name string
Callback func(ndata *navData)
Callback func(ndata *navData) error
}
// loadedDatabase is a DB-agnostic interface for each loaded database.
@ -19,7 +19,7 @@ type loadedDatabase interface {
DisplayName() string
DriverName() string
RootElement() *vcl.TTreeNode
RenderForNav(f *TMainForm, ndata *navData)
RenderForNav(f *TMainForm, ndata *navData) error
ApplyChanges(f *TMainForm, ndata *navData) error
ExecQuery(query string, resultArea *vcl.TStringGrid) error
NavChildren(ndata *navData) ([]string, error)

13
main.go
View File

@ -507,7 +507,10 @@ func (f *TMainForm) OnNavContextPopup(sender vcl.IObject, mousePos types.TPoint,
mnuAction.SetCaption(action.Name)
cb := action.Callback // Copy to avoid reuse of loop variable
mnuAction.SetOnClick(func(sender vcl.IObject) {
cb(ndata)
err = cb(ndata)
if err != nil {
vcl.ShowMessage(err.Error())
}
f.OnNavContextRefresh(curItem, ndata)
})
mnu.Items().Add(mnuAction)
@ -779,7 +782,13 @@ func (f *TMainForm) OnNavChange(sender vcl.IObject, node *vcl.TTreeNode) {
f.propertiesBox.Clear()
vcl_stringgrid_clear(f.contentBox)
ld.RenderForNav(f, ndata) // Handover to the database type's own renderer function
err := ld.RenderForNav(f, ndata) // Handover to the database type's own renderer function
if err != nil {
vcl.ShowMessage(err.Error())
// Ensure elements are disabled
f.contentBox.SetEnabled(false)
}
// We're in charge of common status bar text updates
f.StatusBar.SetSimpleText(ld.DisplayName() + " | " + ld.DriverName())