Table.Columns/Constraints/Indexes/Relationships are Go maps, and every writer, reader, diff, inspector, and merge code path that iterated them directly was subject to Go's randomized map order, so identical input could produce different output (or a different in-report violation/diff order) on every run. Most visibly this showed up as bun/gorm `unique:` struct tags changing order across consecutive `make models` runs with no source change. Fixed by sorting map iteration (by Sequence then Name, or alphabetically for string-keyed maps) everywhere the order affects generated output or first-match tie-break logic, across the bun, gorm, sqlite, dbml, drawdb, pgsql, prisma, graphql, typeorm, drizzle, and dctx writers; the dctx, prisma, and typeorm readers; the shared models.GetPrimaryKey/ GetForeignKeys helpers; pkg/diff, pkg/inspector, and pkg/merge; and the TUI column/relationship pickers in pkg/ui.
76 lines
1.7 KiB
Go
76 lines
1.7 KiB
Go
package ui
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"github.com/rivo/tview"
|
|
|
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
|
)
|
|
|
|
// SchemaEditor represents the interactive schema editor
|
|
type SchemaEditor struct {
|
|
db *models.Database
|
|
app *tview.Application
|
|
pages *tview.Pages
|
|
loadConfig *LoadConfig
|
|
saveConfig *SaveConfig
|
|
}
|
|
|
|
// NewSchemaEditor creates a new schema editor
|
|
func NewSchemaEditor(db *models.Database) *SchemaEditor {
|
|
return &SchemaEditor{
|
|
db: db,
|
|
app: tview.NewApplication(),
|
|
pages: tview.NewPages(),
|
|
loadConfig: nil,
|
|
saveConfig: nil,
|
|
}
|
|
}
|
|
|
|
// NewSchemaEditorWithConfigs creates a new schema editor with load/save configurations
|
|
func NewSchemaEditorWithConfigs(db *models.Database, loadConfig *LoadConfig, saveConfig *SaveConfig) *SchemaEditor {
|
|
return &SchemaEditor{
|
|
db: db,
|
|
app: tview.NewApplication(),
|
|
pages: tview.NewPages(),
|
|
loadConfig: loadConfig,
|
|
saveConfig: saveConfig,
|
|
}
|
|
}
|
|
|
|
// Run starts the interactive editor
|
|
func (se *SchemaEditor) Run() error {
|
|
// If no database is loaded, show load screen
|
|
if se.db == nil {
|
|
se.showLoadScreen()
|
|
} else {
|
|
// Create main menu view
|
|
mainMenu := se.createMainMenu()
|
|
se.pages.AddPage("main", mainMenu, true, true)
|
|
}
|
|
|
|
// Run the application
|
|
if err := se.app.SetRoot(se.pages, true).Run(); err != nil {
|
|
return fmt.Errorf("application error: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetDatabase returns the current database
|
|
func (se *SchemaEditor) GetDatabase() *models.Database {
|
|
return se.db
|
|
}
|
|
|
|
// Helper function to get sorted column names
|
|
func getColumnNames(table *models.Table) []string {
|
|
names := make([]string, 0, len(table.Columns))
|
|
for name := range table.Columns {
|
|
names = append(names, name)
|
|
}
|
|
sort.Strings(names)
|
|
return names
|
|
}
|