package main import ( "context" "fmt" "path/filepath" "unsafe" "github.com/cockroachdb/pebble" "github.com/cockroachdb/pebble/vfs" "github.com/ying32/govcl/vcl" ) 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) error { ctx := context.Background() // Load properties content := fmt.Sprintf("Pebble metrics: %#v", ld.db.Metrics()) f.propertiesBox.SetText(content) // Load data // pebble always uses Key + Value as the columns colKey := f.contentBox.Columns().Add() colKey.Title().SetCaption("Key") colVal := f.contentBox.Columns().Add() colVal.Title().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 { return fmt.Errorf("Failed to load data for key %q: %w", formatAny(k), err) } rpos := f.contentBox.RowCount() f.contentBox.SetRowCount(rpos + 1) f.contentBox.SetCells(0, rpos, formatUtf8(k)) f.contentBox.SetCells(0, rpos, formatUtf8(v)) } // Valid vcl_stringgrid_columnwidths(f.contentBox) f.contentBox.SetEnabled(true) return nil } 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) NavContext(ndata *navData) ([]contextAction, error) { return nil, nil // No special actions are supported } 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) }