Merge pull request #12 from lmika/chore/ctrlret

ctrlret: replaced return types of controllers from tea.Cmd to tea.Msg
This commit is contained in:
Leon Mika 2022-08-18 21:45:04 +10:00 committed by GitHub
commit f0bd3022cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 472 additions and 562 deletions

View file

@ -50,7 +50,7 @@ func main() {
cmdController := commandctrl.NewCommandController() cmdController := commandctrl.NewCommandController()
cmdController.AddCommands(&commandctrl.CommandContext{ cmdController.AddCommands(&commandctrl.CommandContext{
Commands: map[string]commandctrl.Command{ Commands: map[string]commandctrl.Command{
"cd": func(args []string) tea.Cmd { "cd": func(args []string) tea.Msg {
return ctrl.ChangePrefix(args[0]) return ctrl.ChangePrefix(args[0])
}, },
}, },

View file

@ -25,18 +25,16 @@ func (c *CommandController) AddCommands(ctx *CommandContext) {
c.commandList = ctx c.commandList = ctx
} }
func (c *CommandController) Prompt() tea.Cmd { func (c *CommandController) Prompt() tea.Msg {
return func() tea.Msg { return events.PromptForInputMsg{
return events.PromptForInputMsg{ Prompt: ":",
Prompt: ":", OnDone: func(value string) tea.Msg {
OnDone: func(value string) tea.Cmd { return c.Execute(value)
return c.Execute(value) },
},
}
} }
} }
func (c *CommandController) Execute(commandInput string) tea.Cmd { func (c *CommandController) Execute(commandInput string) tea.Msg {
input := strings.TrimSpace(commandInput) input := strings.TrimSpace(commandInput)
if input == "" { if input == "" {
return nil return nil
@ -46,18 +44,18 @@ func (c *CommandController) Execute(commandInput string) tea.Cmd {
command := c.lookupCommand(tokens[0]) command := c.lookupCommand(tokens[0])
if command == nil { if command == nil {
log.Println("No such command: ", tokens) log.Println("No such command: ", tokens)
return events.SetError(errors.New("no such command: " + tokens[0])) return events.Error(errors.New("no such command: " + tokens[0]))
} }
return command(tokens[1:]) return command(tokens[1:])
} }
func (c *CommandController) Alias(commandName string) Command { func (c *CommandController) Alias(commandName string) Command {
return func(args []string) tea.Cmd { return func(args []string) tea.Msg {
command := c.lookupCommand(commandName) command := c.lookupCommand(commandName)
if command == nil { if command == nil {
log.Println("No such command: ", commandName) log.Println("No such command: ", commandName)
return events.SetError(errors.New("no such command: " + commandName)) return events.Error(errors.New("no such command: " + commandName))
} }
return command(args) return command(args)

View file

@ -1,10 +1,10 @@
package commandctrl_test package commandctrl_test
import ( import (
"github.com/lmika/audax/internal/common/ui/events"
"testing" "testing"
"github.com/lmika/audax/internal/common/ui/commandctrl" "github.com/lmika/audax/internal/common/ui/commandctrl"
"github.com/lmika/audax/internal/common/ui/events"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -12,7 +12,7 @@ func TestCommandController_Prompt(t *testing.T) {
t.Run("prompt user for a command", func(t *testing.T) { t.Run("prompt user for a command", func(t *testing.T) {
cmd := commandctrl.NewCommandController() cmd := commandctrl.NewCommandController()
res := cmd.Prompt()() res := cmd.Prompt()
promptForInputMsg, ok := res.(events.PromptForInputMsg) promptForInputMsg, ok := res.(events.PromptForInputMsg)
assert.True(t, ok) assert.True(t, ok)

View file

@ -2,16 +2,16 @@ package commandctrl
import tea "github.com/charmbracelet/bubbletea" import tea "github.com/charmbracelet/bubbletea"
type Command func(args []string) tea.Cmd type Command func(args []string) tea.Msg
func NoArgCommand(cmd tea.Cmd) Command { func NoArgCommand(cmd tea.Cmd) Command {
return func(args []string) tea.Cmd { return func(args []string) tea.Msg {
return cmd return cmd()
} }
} }
type CommandContext struct { type CommandContext struct {
Commands map[string]Command Commands map[string]Command
parent *CommandContext parent *CommandContext
} }

View file

@ -10,29 +10,19 @@ func Error(err error) tea.Msg {
return ErrorMsg(err) return ErrorMsg(err)
} }
func SetError(err error) tea.Cmd { func SetStatus(msg string) tea.Msg {
return func() tea.Msg { return StatusMsg(msg)
return Error(err) }
func PromptForInput(prompt string, onDone func(value string) tea.Msg) tea.Msg {
return PromptForInputMsg{
Prompt: prompt,
OnDone: onDone,
} }
} }
func SetStatus(msg string) tea.Cmd { func Confirm(prompt string, onYes func() tea.Msg) tea.Msg {
return func() tea.Msg { return PromptForInput(prompt, func(value string) tea.Msg {
return StatusMsg(msg)
}
}
func PromptForInput(prompt string, onDone func(value string) tea.Cmd) tea.Cmd {
return func() tea.Msg {
return PromptForInputMsg{
Prompt: prompt,
OnDone: onDone,
}
}
}
func Confirm(prompt string, onYes func() tea.Cmd) tea.Cmd {
return PromptForInput(prompt, func(value string) tea.Cmd {
if value == "y" { if value == "y" {
return onYes() return onYes()
} }

View file

@ -16,5 +16,5 @@ type ModeMessage string
// PromptForInput indicates that the context is requesting a line of input // PromptForInput indicates that the context is requesting a line of input
type PromptForInputMsg struct { type PromptForInputMsg struct {
Prompt string Prompt string
OnDone func(value string) tea.Cmd OnDone func(value string) tea.Msg
} }

View file

@ -15,9 +15,9 @@ func (ps *promptSequence) next() tea.Msg {
if len(ps.receivedValues) < len(ps.prompts) { if len(ps.receivedValues) < len(ps.prompts) {
return events.PromptForInputMsg{ return events.PromptForInputMsg{
Prompt: ps.prompts[len(ps.receivedValues)], Prompt: ps.prompts[len(ps.receivedValues)],
OnDone: func(value string) tea.Cmd { OnDone: func(value string) tea.Msg {
ps.receivedValues = append(ps.receivedValues, value) ps.receivedValues = append(ps.receivedValues, value)
return ps.next return ps.next()
}, },
} }
} }

View file

@ -46,7 +46,7 @@ type SetReadWrite struct {
type PromptForTableMsg struct { type PromptForTableMsg struct {
Tables []string Tables []string
OnSelected func(tableName string) tea.Cmd OnSelected func(tableName string) tea.Msg
} }
type ResultSetUpdated struct { type ResultSetUpdated struct {

View file

@ -35,7 +35,7 @@ func NewTableReadController(state *State, tableService TableReadService, workspa
} }
// 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.Msg {
if c.tableName == "" { if c.tableName == "" {
return c.ListTables() return c.ListTables()
} else { } else {
@ -43,51 +43,43 @@ func (c *TableReadController) Init() tea.Cmd {
} }
} }
func (c *TableReadController) ListTables() tea.Cmd { func (c *TableReadController) ListTables() 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 { return events.Error(err)
return events.Error(err) }
}
return PromptForTableMsg{ return PromptForTableMsg{
Tables: tables, Tables: tables,
OnSelected: func(tableName string) tea.Cmd { OnSelected: func(tableName string) tea.Msg {
return c.ScanTable(tableName) return c.ScanTable(tableName)
}, },
}
} }
} }
func (c *TableReadController) ScanTable(name string) tea.Cmd { func (c *TableReadController) ScanTable(name string) tea.Msg {
return func() tea.Msg { ctx := context.Background()
ctx := context.Background()
tableInfo, err := c.tableService.Describe(ctx, name) tableInfo, err := c.tableService.Describe(ctx, name)
if err != nil { if err != nil {
return events.Error(errors.Wrapf(err, "cannot describe %v", c.tableName)) return events.Error(errors.Wrapf(err, "cannot describe %v", c.tableName))
}
resultSet, err := c.tableService.Scan(ctx, tableInfo)
if err != nil {
return events.Error(err)
}
resultSet = c.tableService.Filter(resultSet, c.state.Filter())
return c.setResultSetAndFilter(resultSet, c.state.Filter(), true)
} }
resultSet, err := c.tableService.Scan(ctx, tableInfo)
if err != nil {
return events.Error(err)
}
resultSet = c.tableService.Filter(resultSet, c.state.Filter())
return c.setResultSetAndFilter(resultSet, c.state.Filter(), true)
} }
func (c *TableReadController) PromptForQuery() tea.Cmd { func (c *TableReadController) PromptForQuery() tea.Msg {
return func() tea.Msg { return events.PromptForInputMsg{
return events.PromptForInputMsg{ Prompt: "query: ",
Prompt: "query: ", OnDone: func(value string) tea.Msg {
OnDone: func(value string) tea.Cmd { return c.runQuery(c.state.ResultSet().TableInfo, value, "", true)
return func() tea.Msg { },
return c.runQuery(c.state.ResultSet().TableInfo, value, "", true)
}
},
}
} }
} }
@ -107,7 +99,7 @@ func (c *TableReadController) runQuery(tableInfo *models.TableInfo, query, newFi
expr, err := queryexpr.Parse(query) expr, err := queryexpr.Parse(query)
if err != nil { if err != nil {
return events.SetError(err) return events.Error(err)
} }
return c.doIfNoneDirty(func() tea.Msg { return c.doIfNoneDirty(func() tea.Msg {
@ -135,58 +127,54 @@ func (c *TableReadController) doIfNoneDirty(cmd tea.Cmd) tea.Msg {
return events.PromptForInputMsg{ return events.PromptForInputMsg{
Prompt: "reset modified items? ", Prompt: "reset modified items? ",
OnDone: func(value string) tea.Cmd { OnDone: func(value string) tea.Msg {
if value != "y" { if value != "y" {
return events.SetStatus("operation aborted") return events.SetStatus("operation aborted")
} }
return cmd return cmd()
}, },
} }
} }
func (c *TableReadController) Rescan() tea.Cmd { func (c *TableReadController) Rescan() tea.Msg {
return func() tea.Msg { return c.doIfNoneDirty(func() tea.Msg {
return c.doIfNoneDirty(func() tea.Msg { resultSet := c.state.ResultSet()
resultSet := c.state.ResultSet() return c.doScan(context.Background(), resultSet, resultSet.Query, true)
return c.doScan(context.Background(), resultSet, resultSet.Query, true) })
})
}
} }
func (c *TableReadController) ExportCSV(filename string) tea.Cmd { func (c *TableReadController) ExportCSV(filename string) tea.Msg {
return func() tea.Msg { resultSet := c.state.ResultSet()
resultSet := c.state.ResultSet() if resultSet == nil {
if resultSet == nil { return events.Error(errors.New("no result set"))
return events.Error(errors.New("no result set"))
}
f, err := os.Create(filename)
if err != nil {
return events.Error(errors.Wrapf(err, "cannot export to '%v'", filename))
}
defer f.Close()
cw := csv.NewWriter(f)
defer cw.Flush()
columns := resultSet.Columns()
if err := cw.Write(columns); err != nil {
return events.Error(errors.Wrapf(err, "cannot export to '%v'", filename))
}
row := make([]string, len(columns))
for _, item := range resultSet.Items() {
for i, col := range columns {
row[i], _ = item.AttributeValueAsString(col)
}
if err := cw.Write(row); err != nil {
return events.Error(errors.Wrapf(err, "cannot export to '%v'", filename))
}
}
return nil
} }
f, err := os.Create(filename)
if err != nil {
return events.Error(errors.Wrapf(err, "cannot export to '%v'", filename))
}
defer f.Close()
cw := csv.NewWriter(f)
defer cw.Flush()
columns := resultSet.Columns()
if err := cw.Write(columns); err != nil {
return events.Error(errors.Wrapf(err, "cannot export to '%v'", filename))
}
row := make([]string, len(columns))
for _, item := range resultSet.Items() {
for i, col := range columns {
row[i], _ = item.AttributeValueAsString(col)
}
if err := cw.Write(row); err != nil {
return events.Error(errors.Wrapf(err, "cannot export to '%v'", filename))
}
}
return nil
} }
func (c *TableReadController) doScan(ctx context.Context, resultSet *models.ResultSet, query models.Queryable, pushBackstack bool) tea.Msg { func (c *TableReadController) doScan(ctx context.Context, resultSet *models.ResultSet, query models.Queryable, pushBackstack bool) tea.Msg {
@ -211,66 +199,58 @@ func (c *TableReadController) setResultSetAndFilter(resultSet *models.ResultSet,
return c.state.buildNewResultSetMessage("") return c.state.buildNewResultSetMessage("")
} }
func (c *TableReadController) Unmark() tea.Cmd { func (c *TableReadController) Unmark() tea.Msg {
return func() tea.Msg { c.state.withResultSet(func(resultSet *models.ResultSet) {
c.state.withResultSet(func(resultSet *models.ResultSet) { for i := range resultSet.Items() {
for i := range resultSet.Items() { resultSet.SetMark(i, false)
resultSet.SetMark(i, false)
}
})
return ResultSetUpdated{}
}
}
func (c *TableReadController) Filter() tea.Cmd {
return func() tea.Msg {
return events.PromptForInputMsg{
Prompt: "filter: ",
OnDone: func(value string) tea.Cmd {
return func() tea.Msg {
resultSet := c.state.ResultSet()
newResultSet := c.tableService.Filter(resultSet, value)
return c.setResultSetAndFilter(newResultSet, value, true)
}
},
} }
})
return ResultSetUpdated{}
}
func (c *TableReadController) Filter() tea.Msg {
return events.PromptForInputMsg{
Prompt: "filter: ",
OnDone: func(value string) tea.Msg {
resultSet := c.state.ResultSet()
newResultSet := c.tableService.Filter(resultSet, value)
return c.setResultSetAndFilter(newResultSet, value, true)
},
} }
} }
func (c *TableReadController) ViewBack() tea.Cmd { func (c *TableReadController) ViewBack() tea.Msg {
return func() tea.Msg { viewSnapshot, err := c.workspaceService.PopSnapshot()
viewSnapshot, err := c.workspaceService.PopSnapshot() if err != nil {
return events.Error(err)
} else if viewSnapshot == nil {
return events.StatusMsg("Backstack is empty")
}
currentResultSet := c.state.ResultSet()
var currentQueryExpr string
if currentResultSet.Query != nil {
currentQueryExpr = currentResultSet.Query.String()
}
if viewSnapshot.TableName == currentResultSet.TableInfo.Name && viewSnapshot.Query == currentQueryExpr {
log.Printf("backstack: setting filter to '%v'", viewSnapshot.Filter)
newResultSet := c.tableService.Filter(currentResultSet, viewSnapshot.Filter)
return c.setResultSetAndFilter(newResultSet, viewSnapshot.Filter, false)
}
tableInfo := currentResultSet.TableInfo
if viewSnapshot.TableName != currentResultSet.TableInfo.Name {
tableInfo, err = c.tableService.Describe(context.Background(), viewSnapshot.TableName)
if err != nil { if err != nil {
return events.Error(err) return events.Error(err)
} else if viewSnapshot == nil {
return events.StatusMsg("Backstack is empty")
} }
currentResultSet := c.state.ResultSet()
var currentQueryExpr string
if currentResultSet.Query != nil {
currentQueryExpr = currentResultSet.Query.String()
}
if viewSnapshot.TableName == currentResultSet.TableInfo.Name && viewSnapshot.Query == currentQueryExpr {
log.Printf("backstack: setting filter to '%v'", viewSnapshot.Filter)
newResultSet := c.tableService.Filter(currentResultSet, viewSnapshot.Filter)
return c.setResultSetAndFilter(newResultSet, viewSnapshot.Filter, false)
}
tableInfo := currentResultSet.TableInfo
if viewSnapshot.TableName != currentResultSet.TableInfo.Name {
tableInfo, err = c.tableService.Describe(context.Background(), viewSnapshot.TableName)
if err != nil {
return events.Error(err)
}
}
log.Printf("backstack: running query: table = '%v', query = '%v', filter = '%v'",
tableInfo.Name, viewSnapshot.Query, viewSnapshot.Filter)
return c.runQuery(tableInfo, viewSnapshot.Query, viewSnapshot.Filter, false)
} }
log.Printf("backstack: running query: table = '%v', query = '%v', filter = '%v'",
tableInfo.Name, viewSnapshot.Query, viewSnapshot.Filter)
return c.runQuery(tableInfo, viewSnapshot.Query, viewSnapshot.Filter, false)
} }

View file

@ -29,8 +29,7 @@ func TestTableReadController_InitTable(t *testing.T) {
t.Run("should prompt for table if no table name provided", func(t *testing.T) { t.Run("should prompt for table if no table name provided", func(t *testing.T) {
readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "") readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "")
cmd := readController.Init() event := readController.Init()
event := cmd()
assert.IsType(t, controllers.PromptForTableMsg{}, event) assert.IsType(t, controllers.PromptForTableMsg{}, event)
}) })
@ -38,8 +37,7 @@ func TestTableReadController_InitTable(t *testing.T) {
t.Run("should scan table if table name provided", func(t *testing.T) { t.Run("should scan table if table name provided", func(t *testing.T) {
readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "") readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "")
cmd := readController.Init() event := readController.Init()
event := cmd()
assert.IsType(t, controllers.PromptForTableMsg{}, event) assert.IsType(t, controllers.PromptForTableMsg{}, event)
}) })
@ -56,13 +54,11 @@ func TestTableReadController_ListTables(t *testing.T) {
readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "") readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "")
t.Run("returns a list of tables", func(t *testing.T) { t.Run("returns a list of tables", func(t *testing.T) {
cmd := readController.ListTables() event := readController.ListTables().(controllers.PromptForTableMsg)
event := cmd().(controllers.PromptForTableMsg)
assert.Equal(t, []string{"alpha-table", "bravo-table"}, event.Tables) assert.Equal(t, []string{"alpha-table", "bravo-table"}, event.Tables)
selectedCmd := event.OnSelected("alpha-table") selectedEvent := event.OnSelected("alpha-table")
selectedEvent := selectedCmd()
resultSet := selectedEvent.(controllers.NewResultSet) resultSet := selectedEvent.(controllers.NewResultSet)
assert.Equal(t, "alpha-table", resultSet.ResultSet.TableInfo.Name) assert.Equal(t, "alpha-table", resultSet.ResultSet.TableInfo.Name)
@ -208,9 +204,7 @@ func testWorkspace(t *testing.T) *workspaces.Workspace {
return ws return ws
} }
func invokeCommand(t *testing.T, cmd tea.Cmd) tea.Msg { func invokeCommand(t *testing.T, msg tea.Msg) tea.Msg {
msg := cmd()
err, isErr := msg.(events.ErrorMsg) err, isErr := msg.(events.ErrorMsg)
if isErr { if isErr {
assert.Fail(t, fmt.Sprintf("expected no error but got one: %v", err)) assert.Fail(t, fmt.Sprintf("expected no error but got one: %v", err))
@ -218,9 +212,7 @@ func invokeCommand(t *testing.T, cmd tea.Cmd) tea.Msg {
return msg return msg
} }
func invokeCommandWithPrompt(t *testing.T, cmd tea.Cmd, promptValue string) { func invokeCommandWithPrompt(t *testing.T, msg tea.Msg, promptValue string) {
msg := cmd()
pi, isPi := msg.(events.PromptForInputMsg) pi, isPi := msg.(events.PromptForInputMsg)
if !isPi { if !isPi {
assert.Fail(t, fmt.Sprintf("expected prompt for input but didn't get one")) assert.Fail(t, fmt.Sprintf("expected prompt for input but didn't get one"))
@ -229,22 +221,18 @@ func invokeCommandWithPrompt(t *testing.T, cmd tea.Cmd, promptValue string) {
invokeCommand(t, pi.OnDone(promptValue)) invokeCommand(t, pi.OnDone(promptValue))
} }
func invokeCommandWithPrompts(t *testing.T, cmd tea.Cmd, promptValues ...string) { func invokeCommandWithPrompts(t *testing.T, msg tea.Msg, promptValues ...string) {
msg := cmd()
for _, promptValue := range promptValues { for _, promptValue := range promptValues {
pi, isPi := msg.(events.PromptForInputMsg) pi, isPi := msg.(events.PromptForInputMsg)
if !isPi { if !isPi {
assert.Fail(t, fmt.Sprintf("expected prompt for input but didn't get one")) assert.Fail(t, fmt.Sprintf("expected prompt for input but didn't get one: %T", msg))
} }
msg = invokeCommand(t, pi.OnDone(promptValue)) msg = invokeCommand(t, pi.OnDone(promptValue))
} }
} }
func invokeCommandWithPromptsExpectingError(t *testing.T, cmd tea.Cmd, promptValues ...string) { func invokeCommandWithPromptsExpectingError(t *testing.T, msg tea.Msg, promptValues ...string) {
msg := cmd()
for _, promptValue := range promptValues { for _, promptValue := range promptValues {
pi, isPi := msg.(events.PromptForInputMsg) pi, isPi := msg.(events.PromptForInputMsg)
if !isPi { if !isPi {
@ -258,9 +246,7 @@ func invokeCommandWithPromptsExpectingError(t *testing.T, cmd tea.Cmd, promptVal
assert.True(t, isErr) assert.True(t, isErr)
} }
func invokeCommandExpectingError(t *testing.T, cmd tea.Cmd) { func invokeCommandExpectingError(t *testing.T, msg tea.Msg) {
msg := cmd()
_, isErr := msg.(events.ErrorMsg) _, isErr := msg.(events.ErrorMsg)
assert.True(t, isErr) assert.True(t, isErr)
} }

View file

@ -27,50 +27,46 @@ func NewTableWriteController(state *State, tableService *tables.Service, tableRe
} }
} }
func (twc *TableWriteController) ToggleMark(idx int) tea.Cmd { func (twc *TableWriteController) ToggleMark(idx int) tea.Msg {
return func() tea.Msg { twc.state.withResultSet(func(resultSet *models.ResultSet) {
twc.state.withResultSet(func(resultSet *models.ResultSet) { resultSet.SetMark(idx, !resultSet.Marked(idx))
resultSet.SetMark(idx, !resultSet.Marked(idx)) })
})
return ResultSetUpdated{} return ResultSetUpdated{}
}
} }
func (twc *TableWriteController) NewItem() tea.Cmd { func (twc *TableWriteController) NewItem() tea.Msg {
return func() tea.Msg { // Work out which keys we need to prompt for
// Work out which keys we need to prompt for rs := twc.state.ResultSet()
rs := twc.state.ResultSet()
keyPrompts := &promptSequence{ keyPrompts := &promptSequence{
prompts: []string{rs.TableInfo.Keys.PartitionKey + ": "}, prompts: []string{rs.TableInfo.Keys.PartitionKey + ": "},
} }
if rs.TableInfo.Keys.SortKey != "" { if rs.TableInfo.Keys.SortKey != "" {
keyPrompts.prompts = append(keyPrompts.prompts, rs.TableInfo.Keys.SortKey+": ") keyPrompts.prompts = append(keyPrompts.prompts, rs.TableInfo.Keys.SortKey+": ")
} }
keyPrompts.onAllDone = func(values []string) tea.Msg { keyPrompts.onAllDone = func(values []string) tea.Msg {
twc.state.withResultSet(func(set *models.ResultSet) { twc.state.withResultSet(func(set *models.ResultSet) {
newItem := models.Item{} newItem := models.Item{}
// TODO: deal with keys of different type // TODO: deal with keys of different type
newItem[rs.TableInfo.Keys.PartitionKey] = &types.AttributeValueMemberS{Value: values[0]} newItem[rs.TableInfo.Keys.PartitionKey] = &types.AttributeValueMemberS{Value: values[0]}
if len(values) == 2 { if len(values) == 2 {
newItem[rs.TableInfo.Keys.SortKey] = &types.AttributeValueMemberS{Value: values[1]} newItem[rs.TableInfo.Keys.SortKey] = &types.AttributeValueMemberS{Value: values[1]}
} }
set.AddNewItem(newItem, models.ItemAttribute{ set.AddNewItem(newItem, models.ItemAttribute{
New: true, New: true,
Dirty: true, Dirty: true,
})
}) })
return twc.state.buildNewResultSetMessage("New item added") })
} return twc.state.buildNewResultSetMessage("New item added")
return keyPrompts.next()
} }
return keyPrompts.next()
} }
func (twc *TableWriteController) SetAttributeValue(idx int, itemType models.ItemType, key string) tea.Cmd { func (twc *TableWriteController) SetAttributeValue(idx int, itemType models.ItemType, key string) tea.Msg {
apPath := newAttrPath(key) apPath := newAttrPath(key)
var attrValue types.AttributeValue var attrValue types.AttributeValue
@ -78,7 +74,7 @@ func (twc *TableWriteController) SetAttributeValue(idx int, itemType models.Item
attrValue, err = apPath.follow(set.Items()[idx]) attrValue, err = apPath.follow(set.Items()[idx])
return err return err
}); err != nil { }); err != nil {
return events.SetError(err) return events.Error(err)
} }
switch itemType { switch itemType {
@ -91,7 +87,7 @@ func (twc *TableWriteController) SetAttributeValue(idx int, itemType models.Item
case *types.AttributeValueMemberBOOL: case *types.AttributeValueMemberBOOL:
return twc.setBoolValue(idx, apPath) return twc.setBoolValue(idx, apPath)
default: default:
return events.SetError(errors.New("attribute type for key must be set")) return events.Error(errors.New("attribute type for key must be set"))
} }
case models.StringItemType: case models.StringItemType:
return twc.setStringValue(idx, apPath) return twc.setStringValue(idx, apPath)
@ -102,35 +98,31 @@ func (twc *TableWriteController) SetAttributeValue(idx int, itemType models.Item
case models.NullItemType: case models.NullItemType:
return twc.setNullValue(idx, apPath) return twc.setNullValue(idx, apPath)
default: default:
return events.SetError(errors.New("unsupported attribute type")) return events.Error(errors.New("unsupported attribute type"))
} }
} }
func (twc *TableWriteController) setStringValue(idx int, attr attrPath) tea.Cmd { func (twc *TableWriteController) setStringValue(idx int, attr attrPath) tea.Msg {
return func() tea.Msg { return events.PromptForInputMsg{
return events.PromptForInputMsg{ Prompt: "string value: ",
Prompt: "string value: ", OnDone: func(value string) tea.Msg {
OnDone: func(value string) tea.Cmd { if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error {
return func() tea.Msg { if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error {
if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error { if err := attr.setAt(item, &types.AttributeValueMemberS{Value: value}); err != nil {
if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error { return err
if err := attr.setAt(item, &types.AttributeValueMemberS{Value: value}); err != nil {
return err
}
set.SetDirty(idx, true)
return nil
}); err != nil {
return err
}
set.RefreshColumns()
return nil
}); err != nil {
return events.Error(err)
} }
return ResultSetUpdated{} set.SetDirty(idx, true)
return nil
}); err != nil {
return err
} }
}, set.RefreshColumns()
} return nil
}); err != nil {
return events.Error(err)
}
return ResultSetUpdated{}
},
} }
} }
@ -147,294 +139,262 @@ func (twc *TableWriteController) applyToItems(rs *models.ResultSet, selectedInde
return applyFn(selectedIndex, rs.Items()[selectedIndex]) return applyFn(selectedIndex, rs.Items()[selectedIndex])
} }
func (twc *TableWriteController) setNumberValue(idx int, attr attrPath) tea.Cmd { func (twc *TableWriteController) setNumberValue(idx int, attr attrPath) tea.Msg {
return func() tea.Msg { return events.PromptForInputMsg{
return events.PromptForInputMsg{ Prompt: "number value: ",
Prompt: "number value: ", OnDone: func(value string) tea.Msg {
OnDone: func(value string) tea.Cmd { if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error {
return func() tea.Msg { if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error {
if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error { if err := attr.setAt(item, &types.AttributeValueMemberN{Value: value}); err != nil {
if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error { return err
if err := attr.setAt(item, &types.AttributeValueMemberN{Value: value}); err != nil {
return err
}
set.SetDirty(idx, true)
return nil
}); err != nil {
return err
}
set.RefreshColumns()
return nil
}); err != nil {
return events.Error(err)
} }
return ResultSetUpdated{} set.SetDirty(idx, true)
} return nil
}, }); err != nil {
}
}
}
func (twc *TableWriteController) setBoolValue(idx int, attr attrPath) tea.Cmd {
return func() tea.Msg {
return events.PromptForInputMsg{
Prompt: "bool value: ",
OnDone: func(value string) tea.Cmd {
return func() tea.Msg {
b, err := strconv.ParseBool(value)
if err != nil {
return events.Error(err)
}
if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error {
if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error {
if err := attr.setAt(item, &types.AttributeValueMemberBOOL{Value: b}); err != nil {
return err
}
set.SetDirty(idx, true)
return nil
}); err != nil {
return err
}
set.RefreshColumns()
return nil
}); err != nil {
return events.Error(err)
}
return ResultSetUpdated{}
}
},
}
}
}
func (twc *TableWriteController) setNullValue(idx int, attr attrPath) tea.Cmd {
return func() tea.Msg {
if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error {
if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error {
if err := attr.setAt(item, &types.AttributeValueMemberNULL{Value: true}); err != nil {
return err return err
} }
set.SetDirty(idx, true) set.RefreshColumns()
return nil return nil
}); err != nil { }); err != nil {
return err return events.Error(err)
} }
set.RefreshColumns() return ResultSetUpdated{}
return nil },
}); err != nil {
return events.Error(err)
}
return ResultSetUpdated{}
} }
} }
func (twc *TableWriteController) DeleteAttribute(idx int, key string) tea.Cmd { func (twc *TableWriteController) setBoolValue(idx int, attr attrPath) tea.Msg {
return func() tea.Msg { return events.PromptForInputMsg{
// Verify that the expression is valid Prompt: "bool value: ",
apPath := newAttrPath(key) OnDone: func(value string) tea.Msg {
b, err := strconv.ParseBool(value)
if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error {
_, err := apPath.follow(set.Items()[idx])
return err
}); err != nil {
return events.Error(err)
}
if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error {
err := apPath.deleteAt(set.Items()[idx])
if err != nil { if err != nil {
return events.Error(err)
}
if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error {
if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error {
if err := attr.setAt(item, &types.AttributeValueMemberBOOL{Value: b}); err != nil {
return err
}
set.SetDirty(idx, true)
return nil
}); err != nil {
return err
}
set.RefreshColumns()
return nil
}); err != nil {
return events.Error(err)
}
return ResultSetUpdated{}
},
}
}
func (twc *TableWriteController) setNullValue(idx int, attr attrPath) tea.Msg {
if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error {
if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error {
if err := attr.setAt(item, &types.AttributeValueMemberNULL{Value: true}); err != nil {
return err return err
} }
set.SetDirty(idx, true) set.SetDirty(idx, true)
set.RefreshColumns()
return nil return nil
}); err != nil { }); err != nil {
return events.Error(err) return err
}
set.RefreshColumns()
return nil
}); err != nil {
return events.Error(err)
}
return ResultSetUpdated{}
}
func (twc *TableWriteController) DeleteAttribute(idx int, key string) tea.Msg {
// Verify that the expression is valid
apPath := newAttrPath(key)
if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error {
_, err := apPath.follow(set.Items()[idx])
return err
}); err != nil {
return events.Error(err)
}
if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error {
err := apPath.deleteAt(set.Items()[idx])
if err != nil {
return err
} }
return ResultSetUpdated{} set.SetDirty(idx, true)
set.RefreshColumns()
return nil
}); err != nil {
return events.Error(err)
}
return ResultSetUpdated{}
}
func (twc *TableWriteController) PutItem(idx int) tea.Msg {
resultSet := twc.state.ResultSet()
if !resultSet.IsDirty(idx) {
return events.Error(errors.New("item is not dirty"))
}
return events.PromptForInputMsg{
Prompt: "put item? ",
OnDone: func(value string) tea.Msg {
if value != "y" {
return nil
}
if err := twc.tableService.PutItemAt(context.Background(), resultSet, idx); err != nil {
return events.Error(err)
}
return ResultSetUpdated{}
},
} }
} }
func (twc *TableWriteController) PutItem(idx int) tea.Cmd { func (twc *TableWriteController) PutItems() tea.Msg {
return func() tea.Msg { var (
resultSet := twc.state.ResultSet() markedItemCount int
if !resultSet.IsDirty(idx) { )
return events.Error(errors.New("item is not dirty")) var itemsToPut []models.ItemIndex
}
return events.PromptForInputMsg{ twc.state.withResultSet(func(rs *models.ResultSet) {
Prompt: "put item? ", if markedItems := rs.MarkedItems(); len(markedItems) > 0 {
OnDone: func(value string) tea.Cmd { for _, mi := range markedItems {
return func() tea.Msg { markedItemCount += 1
if value != "y" { if rs.IsDirty(mi.Index) {
return nil itemsToPut = append(itemsToPut, mi)
}
if err := twc.tableService.PutItemAt(context.Background(), resultSet, idx); err != nil {
return events.Error(err)
}
return ResultSetUpdated{}
}
},
}
}
}
func (twc *TableWriteController) PutItems() tea.Cmd {
return func() tea.Msg {
var (
markedItemCount int
)
var itemsToPut []models.ItemIndex
twc.state.withResultSet(func(rs *models.ResultSet) {
if markedItems := rs.MarkedItems(); len(markedItems) > 0 {
for _, mi := range markedItems {
markedItemCount += 1
if rs.IsDirty(mi.Index) {
itemsToPut = append(itemsToPut, mi)
}
}
} else {
for i, itm := range rs.Items() {
if rs.IsDirty(i) {
itemsToPut = append(itemsToPut, models.ItemIndex{Item: itm, Index: i})
}
} }
} }
})
if len(itemsToPut) == 0 {
if markedItemCount > 0 {
return events.StatusMsg("no marked items are modified")
} else {
return events.StatusMsg("no items are modified")
}
}
var promptMessage string
if markedItemCount > 0 {
promptMessage = applyToN("put ", len(itemsToPut), "marked item", "marked items", "? ")
} else { } else {
promptMessage = applyToN("put ", len(itemsToPut), "item", "items", "? ") for i, itm := range rs.Items() {
} if rs.IsDirty(i) {
itemsToPut = append(itemsToPut, models.ItemIndex{Item: itm, Index: i})
return events.PromptForInputMsg{
Prompt: promptMessage,
OnDone: func(value string) tea.Cmd {
if value != "y" {
return events.SetStatus("operation aborted")
} }
}
return func() tea.Msg {
if err := twc.state.withResultSetReturningError(func(rs *models.ResultSet) error {
err := twc.tableService.PutSelectedItems(context.Background(), rs, itemsToPut)
if err != nil {
return err
}
return nil
}); err != nil {
return events.Error(err)
}
return ResultSetUpdated{
statusMessage: applyToN("", len(itemsToPut), "item", "item", " put to table"),
}
}
},
} }
})
if len(itemsToPut) == 0 {
if markedItemCount > 0 {
return events.StatusMsg("no marked items are modified")
} else {
return events.StatusMsg("no items are modified")
}
}
var promptMessage string
if markedItemCount > 0 {
promptMessage = applyToN("put ", len(itemsToPut), "marked item", "marked items", "? ")
} else {
promptMessage = applyToN("put ", len(itemsToPut), "item", "items", "? ")
}
return events.PromptForInputMsg{
Prompt: promptMessage,
OnDone: func(value string) tea.Msg {
if value != "y" {
return events.SetStatus("operation aborted")
}
if err := twc.state.withResultSetReturningError(func(rs *models.ResultSet) error {
err := twc.tableService.PutSelectedItems(context.Background(), rs, itemsToPut)
if err != nil {
return err
}
return nil
}); err != nil {
return events.Error(err)
}
return ResultSetUpdated{
statusMessage: applyToN("", len(itemsToPut), "item", "item", " put to table"),
}
},
} }
} }
func (twc *TableWriteController) TouchItem(idx int) tea.Cmd { func (twc *TableWriteController) TouchItem(idx int) tea.Msg {
return func() tea.Msg { resultSet := twc.state.ResultSet()
resultSet := twc.state.ResultSet() if resultSet.IsDirty(idx) {
if resultSet.IsDirty(idx) { return events.Error(errors.New("cannot touch dirty items"))
return events.Error(errors.New("cannot touch dirty items")) }
}
return events.PromptForInputMsg{ return events.PromptForInputMsg{
Prompt: "touch item? ", Prompt: "touch item? ",
OnDone: func(value string) tea.Cmd { OnDone: func(value string) tea.Msg {
return func() tea.Msg { if value != "y" {
if value != "y" { return nil
return nil }
}
if err := twc.tableService.PutItemAt(context.Background(), resultSet, idx); err != nil { if err := twc.tableService.PutItemAt(context.Background(), resultSet, idx); err != nil {
return events.Error(err) return events.Error(err)
} }
return ResultSetUpdated{} return ResultSetUpdated{}
} },
},
}
} }
} }
func (twc *TableWriteController) NoisyTouchItem(idx int) tea.Cmd { func (twc *TableWriteController) NoisyTouchItem(idx int) tea.Msg {
return func() tea.Msg { resultSet := twc.state.ResultSet()
resultSet := twc.state.ResultSet() if resultSet.IsDirty(idx) {
if resultSet.IsDirty(idx) { return events.Error(errors.New("cannot noisy touch dirty items"))
return events.Error(errors.New("cannot noisy touch dirty items")) }
}
return events.PromptForInputMsg{ return events.PromptForInputMsg{
Prompt: "noisy touch item? ", Prompt: "noisy touch item? ",
OnDone: func(value string) tea.Cmd { OnDone: func(value string) tea.Msg {
return func() tea.Msg { ctx := context.Background()
ctx := context.Background()
if value != "y" { if value != "y" {
return nil return nil
} }
item := resultSet.Items()[0] item := resultSet.Items()[0]
if err := twc.tableService.Delete(ctx, resultSet.TableInfo, []models.Item{item}); err != nil { if err := twc.tableService.Delete(ctx, resultSet.TableInfo, []models.Item{item}); err != nil {
return events.Error(err) return events.Error(err)
} }
if err := twc.tableService.Put(ctx, resultSet.TableInfo, item); err != nil { if err := twc.tableService.Put(ctx, resultSet.TableInfo, item); err != nil {
return events.Error(err) return events.Error(err)
} }
return twc.tableReadControllers.doScan(ctx, resultSet, resultSet.Query, false) return twc.tableReadControllers.doScan(ctx, resultSet, resultSet.Query, false)
} },
},
}
} }
} }
func (twc *TableWriteController) DeleteMarked() tea.Cmd { func (twc *TableWriteController) DeleteMarked() tea.Msg {
return func() tea.Msg { resultSet := twc.state.ResultSet()
resultSet := twc.state.ResultSet() markedItems := resultSet.MarkedItems()
markedItems := resultSet.MarkedItems()
if len(markedItems) == 0 { if len(markedItems) == 0 {
return events.StatusMsg("no marked items") return events.StatusMsg("no marked items")
} }
return events.PromptForInputMsg{ return events.PromptForInputMsg{
Prompt: applyToN("delete ", len(markedItems), "item", "items", "? "), Prompt: applyToN("delete ", len(markedItems), "item", "items", "? "),
OnDone: func(value string) tea.Cmd { OnDone: func(value string) tea.Msg {
if value != "y" { if value != "y" {
return events.SetStatus("operation aborted") return events.SetStatus("operation aborted")
} }
return func() tea.Msg { ctx := context.Background()
ctx := context.Background() if err := twc.tableService.Delete(ctx, resultSet.TableInfo, sliceutils.Map(markedItems, func(index models.ItemIndex) models.Item {
if err := twc.tableService.Delete(ctx, resultSet.TableInfo, sliceutils.Map(markedItems, func(index models.ItemIndex) models.Item { return index.Item
return index.Item })); err != nil {
})); err != nil { return events.Error(err)
return events.Error(err) }
}
return twc.tableReadControllers.doScan(ctx, resultSet, resultSet.Query, false) return twc.tableReadControllers.doScan(ctx, resultSet, resultSet.Query, false)
} },
},
}
} }
} }

View file

@ -46,27 +46,27 @@ func NewModel(rc *controllers.TableReadController, wc *controllers.TableWriteCon
cc.AddCommands(&commandctrl.CommandContext{ cc.AddCommands(&commandctrl.CommandContext{
Commands: map[string]commandctrl.Command{ Commands: map[string]commandctrl.Command{
"quit": commandctrl.NoArgCommand(tea.Quit), "quit": commandctrl.NoArgCommand(tea.Quit),
"table": func(args []string) tea.Cmd { "table": func(args []string) tea.Msg {
if len(args) == 0 { if len(args) == 0 {
return rc.ListTables() return rc.ListTables()
} else { } else {
return rc.ScanTable(args[0]) return rc.ScanTable(args[0])
} }
}, },
"export": func(args []string) tea.Cmd { "export": func(args []string) tea.Msg {
if len(args) == 0 { if len(args) == 0 {
return events.SetError(errors.New("expected filename")) return events.Error(errors.New("expected filename"))
} }
return rc.ExportCSV(args[0]) return rc.ExportCSV(args[0])
}, },
"unmark": commandctrl.NoArgCommand(rc.Unmark()), "unmark": commandctrl.NoArgCommand(rc.Unmark),
"delete": commandctrl.NoArgCommand(wc.DeleteMarked()), "delete": commandctrl.NoArgCommand(wc.DeleteMarked),
// TEMP // TEMP
"new-item": commandctrl.NoArgCommand(wc.NewItem()), "new-item": commandctrl.NoArgCommand(wc.NewItem),
"set-attr": func(args []string) tea.Cmd { "set-attr": func(args []string) tea.Msg {
if len(args) == 0 { if len(args) == 0 {
return events.SetError(errors.New("expected field")) return events.Error(errors.New("expected field"))
} }
var itemType = models.UnsetItemType var itemType = models.UnsetItemType
@ -81,27 +81,27 @@ func NewModel(rc *controllers.TableReadController, wc *controllers.TableWriteCon
case "-NULL": case "-NULL":
itemType = models.NullItemType itemType = models.NullItemType
default: default:
return events.SetError(errors.New("unrecognised item type")) return events.Error(errors.New("unrecognised item type"))
} }
args = args[1:] args = args[1:]
} }
return wc.SetAttributeValue(dtv.SelectedItemIndex(), itemType, args[0]) return wc.SetAttributeValue(dtv.SelectedItemIndex(), itemType, args[0])
}, },
"del-attr": func(args []string) tea.Cmd { "del-attr": func(args []string) tea.Msg {
if len(args) == 0 { if len(args) == 0 {
return events.SetError(errors.New("expected field")) return events.Error(errors.New("expected field"))
} }
return wc.DeleteAttribute(dtv.SelectedItemIndex(), args[0]) return wc.DeleteAttribute(dtv.SelectedItemIndex(), args[0])
}, },
"put": func(args []string) tea.Cmd { "put": func(args []string) tea.Msg {
return wc.PutItems() return wc.PutItems()
}, },
"touch": func(args []string) tea.Cmd { "touch": func(args []string) tea.Msg {
return wc.TouchItem(dtv.SelectedItemIndex()) return wc.TouchItem(dtv.SelectedItemIndex())
}, },
"noisy-touch": func(args []string) tea.Cmd { "noisy-touch": func(args []string) tea.Msg {
return wc.NoisyTouchItem(dtv.SelectedItemIndex()) return wc.NoisyTouchItem(dtv.SelectedItemIndex())
}, },
@ -129,7 +129,7 @@ func NewModel(rc *controllers.TableReadController, wc *controllers.TableWriteCon
} }
func (m Model) Init() tea.Cmd { func (m Model) Init() tea.Cmd {
return m.tableReadController.Init() return m.tableReadController.Init
} }
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
@ -141,21 +141,21 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg.String() { switch msg.String() {
case "m": case "m":
if idx := m.tableView.SelectedItemIndex(); idx >= 0 { if idx := m.tableView.SelectedItemIndex(); idx >= 0 {
return m, m.tableWriteController.ToggleMark(idx) return m, func() tea.Msg { return m.tableWriteController.ToggleMark(idx) }
} }
case "R": case "R":
return m, m.tableReadController.Rescan() return m, m.tableReadController.Rescan
case "?": case "?":
return m, m.tableReadController.PromptForQuery() return m, m.tableReadController.PromptForQuery
case "/": case "/":
return m, m.tableReadController.Filter() return m, m.tableReadController.Filter
case "backspace": case "backspace":
return m, m.tableReadController.ViewBack() return m, m.tableReadController.ViewBack
//case "e": //case "e":
// m.itemEdit.Visible() // m.itemEdit.Visible()
// return m, nil // return m, nil
case ":": case ":":
return m, m.commandController.Prompt() return m, m.commandController.Prompt
case "ctrl+c", "esc": case "ctrl+c", "esc":
return m, tea.Quit return m, tea.Quit
} }

View file

@ -67,7 +67,7 @@ func (s *StatusAndPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
pendingInput := s.pendingInput pendingInput := s.pendingInput
s.pendingInput = nil s.pendingInput = nil
return s, pendingInput.OnDone(s.textInput.Value()) return s, func() tea.Msg { return pendingInput.OnDone(s.textInput.Value()) }
default: default:
if msg.Type == tea.KeyRunes { if msg.Type == tea.KeyRunes {
msg.Runes = sliceutils.Filter(msg.Runes, func(r rune) bool { return r != '\x0d' && r != '\x0a' }) msg.Runes = sliceutils.Filter(msg.Runes, func(r rune) bool { return r != '\x0d' && r != '\x0a' })

View file

@ -55,7 +55,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var sel controllers.PromptForTableMsg var sel controllers.PromptForTableMsg
sel, m.pendingSelection = *m.pendingSelection, nil sel, m.pendingSelection = *m.pendingSelection, nil
return m, sel.OnSelected(m.listController.list.SelectedItem().(tableItem).name) return m, func() tea.Msg { return sel.OnSelected(m.listController.list.SelectedItem().(tableItem).name) }
} }
} }

View file

@ -62,7 +62,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg.String() { switch msg.String() {
// TEMP // TEMP
case ":": case ":":
return m, m.cmdController.Prompt() return m, func() tea.Msg { return m.cmdController.Prompt() }
case "w": case "w":
return m, m.controller.ViewLogLineFullScreen(m.logLines.SelectedLogLine()) return m, m.controller.ViewLogLineFullScreen(m.logLines.SelectedLogLine())
// END TEMP // END TEMP

View file

@ -39,26 +39,24 @@ func (c *SSMController) Fetch() tea.Cmd {
} }
} }
func (c *SSMController) ChangePrefix(newPrefix string) tea.Cmd { func (c *SSMController) ChangePrefix(newPrefix string) tea.Msg {
return func() tea.Msg { res, err := c.service.List(context.Background(), newPrefix)
res, err := c.service.List(context.Background(), newPrefix) if err != nil {
if err != nil { return events.Error(err)
return events.Error(err) }
}
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
c.prefix = newPrefix c.prefix = newPrefix
return NewParameterListMsg{ return NewParameterListMsg{
Prefix: c.prefix, Prefix: c.prefix,
Parameters: res, Parameters: res,
}
} }
} }
func (c *SSMController) Clone(param models.SSMParameter) tea.Cmd { func (c *SSMController) Clone(param models.SSMParameter) tea.Msg {
return events.PromptForInput("New key: ", func(value string) tea.Cmd { return events.PromptForInput("New key: ", func(value string) tea.Msg {
return func() tea.Msg { return func() tea.Msg {
ctx := context.Background() ctx := context.Background()
if err := c.service.Clone(ctx, param, value); err != nil { if err := c.service.Clone(ctx, param, value); err != nil {
@ -78,23 +76,21 @@ func (c *SSMController) Clone(param models.SSMParameter) tea.Cmd {
}) })
} }
func (c *SSMController) DeleteParameter(param models.SSMParameter) tea.Cmd { func (c *SSMController) DeleteParameter(param models.SSMParameter) tea.Msg {
return events.Confirm("delete parameter? ", func() tea.Cmd { return events.Confirm("delete parameter? ", func() tea.Msg {
return func() tea.Msg { ctx := context.Background()
ctx := context.Background() if err := c.service.Delete(ctx, param); err != nil {
if err := c.service.Delete(ctx, param); err != nil { return events.Error(err)
return events.Error(err) }
}
res, err := c.service.List(context.Background(), c.prefix) res, err := c.service.List(context.Background(), c.prefix)
if err != nil { if err != nil {
return events.Error(err) return events.Error(err)
} }
return NewParameterListMsg{ return NewParameterListMsg{
Prefix: c.prefix, Prefix: c.prefix,
Parameters: res, Parameters: res,
}
} }
}) })
} }

View file

@ -32,17 +32,17 @@ func NewModel(controller *controllers.SSMController, cmdController *commandctrl.
cmdController.AddCommands(&commandctrl.CommandContext{ cmdController.AddCommands(&commandctrl.CommandContext{
Commands: map[string]commandctrl.Command{ Commands: map[string]commandctrl.Command{
"clone": func(args []string) tea.Cmd { "clone": func(args []string) tea.Msg {
if currentParam := ssmList.CurrentParameter(); currentParam != nil { if currentParam := ssmList.CurrentParameter(); currentParam != nil {
return controller.Clone(*currentParam) return controller.Clone(*currentParam)
} }
return events.SetError(errors.New("no parameter selected")) return events.Error(errors.New("no parameter selected"))
}, },
"delete": func(args []string) tea.Cmd { "delete": func(args []string) tea.Msg {
if currentParam := ssmList.CurrentParameter(); currentParam != nil { if currentParam := ssmList.CurrentParameter(); currentParam != nil {
return controller.DeleteParameter(*currentParam) return controller.DeleteParameter(*currentParam)
} }
return events.SetError(errors.New("no parameter selected")) return events.Error(errors.New("no parameter selected"))
}, },
}, },
}) })
@ -75,7 +75,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg.String() { switch msg.String() {
// TEMP // TEMP
case ":": case ":":
return m, m.cmdController.Prompt() return m, func() tea.Msg { return m.cmdController.Prompt() }
// END TEMP // END TEMP
case "ctrl+c", "q": case "ctrl+c", "q":