package main import ( "context" "fmt" "path/filepath" "unsafe" "github.com/cockroachdb/pebble" "github.com/cockroachdb/pebble/vfs" "github.com/ying32/govcl/vcl" "github.com/ying32/govcl/vcl/types" ) type pebbleLoadedDatabase struct { displayName string db *pebble.DB nav *vcl.TTreeNode arena []*navData // keepalive } func (ld *pebbleLoadedDatabase) DisplayName() string { return ld.displayName } func (ld *pebbleLoadedDatabase) DriverName() string { return "Pebble " + version_Pebble // Parsed from embedded go.mod copy } func (ld *pebbleLoadedDatabase) RootElement() *vcl.TTreeNode { return ld.nav } func (ld *pebbleLoadedDatabase) Keepalive(ndata *navData) { ld.arena = append(ld.arena, ndata) } func (ld *pebbleLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) { ctx := context.Background() // Load properties content := fmt.Sprintf("Pebble metrics: %#v", ld.db.Metrics()) f.propertiesBox.SetText(content) // Load data f.contentBox.SetEnabled(false) f.contentBox.Clear() // pebble always uses Key + Value as the columns f.contentBox.Columns().Clear() colKey := f.contentBox.Columns().Add() colKey.SetCaption("Key") colKey.SetWidth(MY_WIDTH) colKey.SetAlignment(types.TaLeftJustify) colVal := f.contentBox.Columns().Add() colVal.SetCaption("Value") itr := ld.db.NewIterWithContext(ctx, nil) defer itr.Close() for itr.First(); itr.Valid(); itr.Next() { 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 } dataEntry := f.contentBox.Items().Add() dataEntry.SetCaption(formatUtf8(k)) dataEntry.SubItems().Add(formatUtf8(v)) } // Valid f.contentBox.SetEnabled(true) } func (ld *pebbleLoadedDatabase) NavChildren(ndata *navData) ([]string, error) { // In the pebble implementation, there is only one child: "Data" if len(ndata.bucketPath) == 0 { return []string{"Data"}, nil } else { // No children deeper than that return []string{}, nil } } func (ld *pebbleLoadedDatabase) ExecQuery(query string, resultArea *vcl.TListView) { vcl.ShowMessage("pebble doesn't support querying") } func (ld *pebbleLoadedDatabase) Close() { _ = ld.db.Close() ld.arena = nil } var _ loadedDatabase = &pebbleLoadedDatabase{} // interface assertion // func (f *TMainForm) pebbleAddDatabaseFromMemory() { f.pebbleAddDatabaseFrom("", &pebble.Options{FS: vfs.NewMem()}) } func (f *TMainForm) pebbleAddDatabaseFrom(dirname string, opts *pebble.Options) { // TODO load in background thread to stop blocking the UI db, err := pebble.Open(dirname, opts) if err != nil { vcl.ShowMessage(fmt.Sprintf("Failed to load database: %s", err.Error())) return } ld := &pebbleLoadedDatabase{ db: db, displayName: filepath.Base(dirname), } ld.nav = f.Buckets.Items().Add(nil, ld.displayName) ld.nav.SetHasChildren(true) // dynamically populate in OnNavExpanding ld.nav.SetImageIndex(imgDatabase) ld.nav.SetSelectedIndex(imgDatabase) navData := &navData{ ld: ld, childrenLoaded: false, // will be loaded dynamically bucketPath: []string{}, // empty = root } ld.nav.SetData(unsafe.Pointer(navData)) f.dbs = append(f.dbs, ld) f.Buckets.SetSelected(ld.nav) // Select new element ld.Keepalive(navData) }