ssm-browse: fixed the views of dynamo-browse
This commit is contained in:
parent
f6f06eb22d
commit
d3f6475070
|
@ -9,6 +9,7 @@ import (
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/lmika/awstools/internal/common/ui/commandctrl"
|
"github.com/lmika/awstools/internal/common/ui/commandctrl"
|
||||||
|
"github.com/lmika/awstools/internal/common/ui/logging"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/providers/dynamo"
|
"github.com/lmika/awstools/internal/dynamo-browse/providers/dynamo"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
|
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
|
||||||
|
@ -46,10 +47,18 @@ func main() {
|
||||||
tableWriteController := controllers.NewTableWriteController(tableService, tableReadController, *flagTable)
|
tableWriteController := controllers.NewTableWriteController(tableService, tableReadController, *flagTable)
|
||||||
_ = tableWriteController
|
_ = tableWriteController
|
||||||
|
|
||||||
commandController := commandctrl.NewCommandController(map[string]commandctrl.Command{
|
commandController := commandctrl.NewCommandController()
|
||||||
"q": commandctrl.NoArgCommand(tea.Quit),
|
commandController.AddCommands(&commandctrl.CommandContext{
|
||||||
//"rw": tableWriteController.ToggleReadWrite(),
|
Commands: map[string]commandctrl.Command{
|
||||||
//"dup": tableWriteController.Duplicate(),
|
"q": commandctrl.NoArgCommand(tea.Quit),
|
||||||
|
"table": func(args []string) tea.Cmd {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return tableReadController.ListTables()
|
||||||
|
} else {
|
||||||
|
return tableReadController.ScanTable(args[0])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
model := ui.NewModel(tableReadController, commandController)
|
model := ui.NewModel(tableReadController, commandController)
|
||||||
|
@ -59,12 +68,8 @@ func main() {
|
||||||
|
|
||||||
p := tea.NewProgram(model, tea.WithAltScreen())
|
p := tea.NewProgram(model, tea.WithAltScreen())
|
||||||
|
|
||||||
f, err := tea.LogToFile("debug.log", "debug")
|
closeFn := logging.EnableLogging()
|
||||||
if err != nil {
|
defer closeFn()
|
||||||
fmt.Println("fatal:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
log.Println("launching")
|
log.Println("launching")
|
||||||
if err := p.Start(); err != nil {
|
if err := p.Start(); err != nil {
|
||||||
|
@ -72,12 +77,3 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
//type msgLoopback struct {
|
|
||||||
// program *tea.Program
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (m *msgLoopback) Send(msg tea.Msg) {
|
|
||||||
// m.program.Send(msg)
|
|
||||||
//}
|
|
||||||
|
|
|
@ -7,30 +7,36 @@ import (
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
|
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TableReadController struct {
|
type TableReadController struct {
|
||||||
tableService *tables.Service
|
tableService *tables.Service
|
||||||
tableName string
|
tableName string
|
||||||
|
|
||||||
|
// state
|
||||||
|
mutex *sync.Mutex
|
||||||
|
resultSet *models.ResultSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTableReadController(tableService *tables.Service, tableName string) *TableReadController {
|
func NewTableReadController(tableService *tables.Service, tableName string) *TableReadController {
|
||||||
return &TableReadController{
|
return &TableReadController{
|
||||||
tableService: tableService,
|
tableService: tableService,
|
||||||
tableName: tableName,
|
tableName: tableName,
|
||||||
|
mutex: new(sync.Mutex),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init does an initial scan of the table. If no table is specified, it prompts for a table, then does a scan.
|
// Init does an initial scan of the table. If no table is specified, it prompts for a table, then does a scan.
|
||||||
func (c *TableReadController) Init() tea.Cmd {
|
func (c *TableReadController) Init() tea.Cmd {
|
||||||
if c.tableName == "" {
|
if c.tableName == "" {
|
||||||
return c.listTables()
|
return c.ListTables()
|
||||||
} else {
|
} else {
|
||||||
return c.scanTable(c.tableName)
|
return c.ScanTable(c.tableName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TableReadController) listTables() tea.Cmd {
|
func (c *TableReadController) ListTables() tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
tables, err := c.tableService.ListTables(context.Background())
|
tables, err := c.tableService.ListTables(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -40,13 +46,13 @@ func (c *TableReadController) listTables() tea.Cmd {
|
||||||
return PromptForTableMsg{
|
return PromptForTableMsg{
|
||||||
Tables: tables,
|
Tables: tables,
|
||||||
OnSelected: func(tableName string) tea.Cmd {
|
OnSelected: func(tableName string) tea.Cmd {
|
||||||
return c.scanTable(tableName)
|
return c.ScanTable(tableName)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TableReadController) scanTable(name string) tea.Cmd {
|
func (c *TableReadController) ScanTable(name string) tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
@ -60,23 +66,31 @@ func (c *TableReadController) scanTable(name string) tea.Cmd {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewResultSet{resultSet}
|
return c.setResultSet(resultSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TableReadController) Rescan(resultSet *models.ResultSet) tea.Cmd {
|
func (c *TableReadController) Rescan() tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
resultSet, err := c.tableService.Scan(ctx, resultSet.TableInfo)
|
resultSet, err := c.tableService.Scan(ctx, c.resultSet.TableInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewResultSet{resultSet}
|
return c.setResultSet(resultSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *TableReadController) setResultSet(resultSet *models.ResultSet) tea.Msg {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
c.resultSet = resultSet
|
||||||
|
return NewResultSet{resultSet}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
func (c *TableReadController) Scan() uimodels.Operation {
|
func (c *TableReadController) Scan() uimodels.Operation {
|
||||||
return uimodels.OperationFn(func(ctx context.Context) error {
|
return uimodels.OperationFn(func(ctx context.Context) error {
|
||||||
|
|
|
@ -14,23 +14,25 @@ import (
|
||||||
type Model struct {
|
type Model struct {
|
||||||
tableReadController *controllers.TableReadController
|
tableReadController *controllers.TableReadController
|
||||||
commandController *commandctrl.CommandController
|
commandController *commandctrl.CommandController
|
||||||
|
statusAndPrompt *statusandprompt.StatusAndPrompt
|
||||||
|
tableSelect *tableselect.Model
|
||||||
|
|
||||||
root tea.Model
|
root tea.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModel(rc *controllers.TableReadController, cc *commandctrl.CommandController) Model {
|
func NewModel(rc *controllers.TableReadController, cc *commandctrl.CommandController) Model {
|
||||||
dtv := dynamotableview.New(rc, cc)
|
dtv := dynamotableview.New()
|
||||||
div := dynamoitemview.New()
|
div := dynamoitemview.New()
|
||||||
|
statusAndPrompt := statusandprompt.New(layout.NewVBox(layout.LastChildFixedAt(17), dtv, div), "")
|
||||||
|
tableSelect := tableselect.New(statusAndPrompt)
|
||||||
|
|
||||||
m := statusandprompt.New(
|
root := layout.FullScreen(tableSelect)
|
||||||
layout.NewVBox(layout.LastChildFixedAt(17), dtv, div),
|
|
||||||
"Hello world",
|
|
||||||
)
|
|
||||||
root := layout.FullScreen(tableselect.New(m))
|
|
||||||
|
|
||||||
return Model{
|
return Model{
|
||||||
tableReadController: rc,
|
tableReadController: rc,
|
||||||
commandController: cc,
|
commandController: cc,
|
||||||
|
statusAndPrompt: statusAndPrompt,
|
||||||
|
tableSelect: tableSelect,
|
||||||
root: root,
|
root: root,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +42,20 @@ func (m Model) Init() tea.Cmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case tea.KeyMsg:
|
||||||
|
if !m.statusAndPrompt.InPrompt() && !m.tableSelect.Visible() {
|
||||||
|
switch msg.String() {
|
||||||
|
case "s":
|
||||||
|
return m, m.tableReadController.Rescan()
|
||||||
|
case ":":
|
||||||
|
return m, m.commandController.Prompt()
|
||||||
|
case "ctrl+c", "esc":
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
m.root, cmd = m.root.Update(msg)
|
m.root, cmd = m.root.Update(msg)
|
||||||
return m, cmd
|
return m, cmd
|
||||||
|
|
|
@ -25,18 +25,18 @@ type Model struct {
|
||||||
selectedItem models.Item
|
selectedItem models.Item
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() Model {
|
func New() *Model {
|
||||||
return Model{
|
return &Model{
|
||||||
frameTitle: frame.NewFrameTitle("Item", false),
|
frameTitle: frame.NewFrameTitle("Item", false),
|
||||||
viewport: viewport.New(100, 100),
|
viewport: viewport.New(100, 100),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Model) Init() tea.Cmd {
|
func (*Model) Init() tea.Cmd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case NewItemSelected:
|
case NewItemSelected:
|
||||||
m.currentResultSet = msg.ResultSet
|
m.currentResultSet = msg.ResultSet
|
||||||
|
@ -47,14 +47,14 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) View() string {
|
func (m *Model) View() string {
|
||||||
if !m.ready {
|
if !m.ready {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return lipgloss.JoinVertical(lipgloss.Top, m.frameTitle.View(), m.viewport.View())
|
return lipgloss.JoinVertical(lipgloss.Top, m.frameTitle.View(), m.viewport.View())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) Resize(w, h int) layout.ResizingModel {
|
func (m *Model) Resize(w, h int) layout.ResizingModel {
|
||||||
m.w, m.h = w, h
|
m.w, m.h = w, h
|
||||||
if !m.ready {
|
if !m.ready {
|
||||||
m.viewport = viewport.New(w, h-m.frameTitle.HeaderHeight())
|
m.viewport = viewport.New(w, h-m.frameTitle.HeaderHeight())
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
table "github.com/calyptia/go-bubble-table"
|
table "github.com/calyptia/go-bubble-table"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/lmika/awstools/internal/common/ui/commandctrl"
|
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamoitemview"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamoitemview"
|
||||||
|
@ -13,9 +12,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
tableReadControllers *controllers.TableReadController
|
|
||||||
commandCtrl *commandctrl.CommandController
|
|
||||||
|
|
||||||
frameTitle frame.FrameTitle
|
frameTitle frame.FrameTitle
|
||||||
table table.Model
|
table table.Model
|
||||||
w, h int
|
w, h int
|
||||||
|
@ -24,26 +20,24 @@ type Model struct {
|
||||||
resultSet *models.ResultSet
|
resultSet *models.ResultSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(tableReadControllers *controllers.TableReadController, commandCtrl *commandctrl.CommandController) Model {
|
func New() *Model {
|
||||||
tbl := table.New([]string{"pk", "sk"}, 100, 100)
|
tbl := table.New([]string{"pk", "sk"}, 100, 100)
|
||||||
rows := make([]table.Row, 0)
|
rows := make([]table.Row, 0)
|
||||||
tbl.SetRows(rows)
|
tbl.SetRows(rows)
|
||||||
|
|
||||||
frameTitle := frame.NewFrameTitle("No table", true)
|
frameTitle := frame.NewFrameTitle("No table", true)
|
||||||
|
|
||||||
return Model{
|
return &Model{
|
||||||
tableReadControllers: tableReadControllers,
|
|
||||||
commandCtrl: commandCtrl,
|
|
||||||
frameTitle: frameTitle,
|
frameTitle: frameTitle,
|
||||||
table: tbl,
|
table: tbl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) Init() tea.Cmd {
|
func (m *Model) Init() tea.Cmd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case controllers.NewResultSet:
|
case controllers.NewResultSet:
|
||||||
m.resultSet = msg.ResultSet
|
m.resultSet = msg.ResultSet
|
||||||
|
@ -58,26 +52,17 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
case "k", "down":
|
case "k", "down":
|
||||||
m.table.GoDown()
|
m.table.GoDown()
|
||||||
return m, m.postSelectedItemChanged
|
return m, m.postSelectedItemChanged
|
||||||
|
|
||||||
// TEMP
|
|
||||||
case "s":
|
|
||||||
return m, m.tableReadControllers.Rescan(m.resultSet)
|
|
||||||
case ":":
|
|
||||||
return m, m.commandCtrl.Prompt()
|
|
||||||
// END TEMP
|
|
||||||
case "ctrl+c", "esc":
|
|
||||||
return m, tea.Quit
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) View() string {
|
func (m *Model) View() string {
|
||||||
return lipgloss.JoinVertical(lipgloss.Top, m.frameTitle.View(), m.table.View())
|
return lipgloss.JoinVertical(lipgloss.Top, m.frameTitle.View(), m.table.View())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) Resize(w, h int) layout.ResizingModel {
|
func (m *Model) Resize(w, h int) layout.ResizingModel {
|
||||||
m.w, m.h = w, h
|
m.w, m.h = w, h
|
||||||
tblHeight := h - m.frameTitle.HeaderHeight()
|
tblHeight := h - m.frameTitle.HeaderHeight()
|
||||||
m.table.SetSize(w, tblHeight)
|
m.table.SetSize(w, tblHeight)
|
||||||
|
@ -120,31 +105,3 @@ func (m *Model) postSelectedItemChanged() tea.Msg {
|
||||||
|
|
||||||
return dynamoitemview.NewItemSelected{ResultSet: item.resultSet, Item: item.item}
|
return dynamoitemview.NewItemSelected{ResultSet: item.resultSet, Item: item.item}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func (m *Model) updateViewportToSelectedMessage() {
|
|
||||||
selectedItem, ok := m.selectedItem()
|
|
||||||
if !ok {
|
|
||||||
m.viewport.SetContent("(no row selected)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
viewportContent := &strings.Builder{}
|
|
||||||
tabWriter := tabwriter.NewWriter(viewportContent, 0, 1, 1, ' ', 0)
|
|
||||||
for _, colName := range selectedItem.resultSet.Columns {
|
|
||||||
switch colVal := selectedItem.item[colName].(type) {
|
|
||||||
case nil:
|
|
||||||
break
|
|
||||||
case *types.AttributeValueMemberS:
|
|
||||||
fmt.Fprintf(tabWriter, "%v\tS\t%s\n", colName, colVal.Value)
|
|
||||||
case *types.AttributeValueMemberN:
|
|
||||||
fmt.Fprintf(tabWriter, "%v\tN\t%s\n", colName, colVal.Value)
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(tabWriter, "%v\t?\t%s\n", colName, "(other)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tabWriter.Flush()
|
|
||||||
m.viewport.SetContent(viewportContent.String())
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
|
@ -19,16 +19,16 @@ type Model struct {
|
||||||
w, h int
|
w, h int
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(submodel tea.Model) Model {
|
func New(submodel tea.Model) *Model {
|
||||||
frameTitle := frame.NewFrameTitle("Select table", false)
|
frameTitle := frame.NewFrameTitle("Select table", false)
|
||||||
return Model{frameTitle: frameTitle, submodel: submodel}
|
return &Model{frameTitle: frameTitle, submodel: submodel}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) Init() tea.Cmd {
|
func (m *Model) Init() tea.Cmd {
|
||||||
return m.submodel.Init()
|
return m.submodel.Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
var cc utils.CmdCollector
|
var cc utils.CmdCollector
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case controllers.PromptForTableMsg:
|
case controllers.PromptForTableMsg:
|
||||||
|
@ -60,7 +60,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
return m, cc.Cmd()
|
return m, cc.Cmd()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) View() string {
|
func (m *Model) View() string {
|
||||||
if m.pendingSelection != nil {
|
if m.pendingSelection != nil {
|
||||||
return lipgloss.JoinVertical(lipgloss.Top, m.frameTitle.View(), m.listController.View())
|
return lipgloss.JoinVertical(lipgloss.Top, m.frameTitle.View(), m.listController.View())
|
||||||
} else if m.isLoading {
|
} else if m.isLoading {
|
||||||
|
@ -70,11 +70,11 @@ func (m Model) View() string {
|
||||||
return m.submodel.View()
|
return m.submodel.View()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) shouldShow() bool {
|
func (m *Model) Visible() bool {
|
||||||
return m.pendingSelection != nil || m.isLoading
|
return m.pendingSelection != nil || m.isLoading
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) Resize(w, h int) layout.ResizingModel {
|
func (m *Model) Resize(w, h int) layout.ResizingModel {
|
||||||
m.w, m.h = w, h
|
m.w, m.h = w, h
|
||||||
m.submodel = layout.Resize(m.submodel, w, h)
|
m.submodel = layout.Resize(m.submodel, w, h)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue