db: return error from RenderForNav, contextAction.Callback
This commit is contained in:
parent
ee3110162b
commit
e1a9f187cb
@ -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 {
|
||||
|
19
db_bolt.go
19
db_bolt.go
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
25
db_redis.go
25
db_redis.go
@ -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)
|
||||
|
||||
}
|
||||
}
|
||||
|
13
db_sqlite.go
13
db_sqlite.go
@ -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("?")
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
13
main.go
@ -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())
|
||||
|
Loading…
Reference in New Issue
Block a user