211 lines
4.6 KiB
Go
211 lines
4.6 KiB
Go
package cmdpacks
|
|
|
|
import (
|
|
"context"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl"
|
|
"lmika.dev/cmd/dynamo-browse/internal/common/ui/events"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/tables"
|
|
"ucl.lmika.dev/ucl"
|
|
)
|
|
|
|
type uiModule struct {
|
|
tableService *tables.Service
|
|
state *controllers.State
|
|
ckb *customKeyBinding
|
|
readController *controllers.TableReadController
|
|
}
|
|
|
|
func (m *uiModule) uiCommand(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|
var (
|
|
name string
|
|
cmd ucl.Invokable
|
|
)
|
|
if err := args.Bind(&name, &cmd); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
invoker := commandctrl.GetInvoker(ctx)
|
|
invoker.Inst().SetBuiltinInvokable(name, cmd)
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *uiModule) uiPrompt(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|
var prompt string
|
|
if err := args.Bind(&prompt); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resChan := make(chan string)
|
|
go func() {
|
|
commandctrl.PostMsg(ctx, events.PromptForInput(prompt, nil, func(value string) tea.Msg {
|
|
resChan <- value
|
|
return nil
|
|
}))
|
|
}()
|
|
|
|
select {
|
|
case value := <-resChan:
|
|
return value, nil
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
}
|
|
}
|
|
|
|
func (m *uiModule) uiConfirm(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|
var prompt string
|
|
if err := args.Bind(&prompt); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resChan := make(chan bool)
|
|
go func() {
|
|
commandctrl.PostMsg(ctx, events.Confirm(prompt, func(value bool) tea.Msg {
|
|
resChan <- value
|
|
return nil
|
|
}))
|
|
}()
|
|
|
|
select {
|
|
case value := <-resChan:
|
|
return value, nil
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
}
|
|
}
|
|
|
|
func (m *uiModule) uiPromptTable(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|
tables, err := m.tableService.ListTables(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resChan := make(chan string)
|
|
go func() {
|
|
commandctrl.PostMsg(ctx, controllers.PromptForTableMsg{
|
|
Tables: tables,
|
|
OnSelected: func(tableName string) tea.Msg {
|
|
resChan <- tableName
|
|
return nil
|
|
},
|
|
})
|
|
}()
|
|
|
|
select {
|
|
case value := <-resChan:
|
|
return value, nil
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
}
|
|
}
|
|
|
|
func (m *uiModule) uiBind(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|
var (
|
|
bindName string
|
|
key string
|
|
inv ucl.Invokable
|
|
)
|
|
|
|
if args.NArgs() == 2 {
|
|
if err := args.Bind(&key, &inv); err != nil {
|
|
return nil, err
|
|
}
|
|
bindName = "custom." + key
|
|
} else {
|
|
if err := args.Bind(&bindName, &key, &inv); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
invoker := commandctrl.GetInvoker(ctx)
|
|
|
|
m.ckb.bindings[bindName] = func() tea.Msg {
|
|
return invoker.Invoke(inv, nil)
|
|
}
|
|
m.ckb.keyBindings[key] = bindName
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *uiModule) uiQuery(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|
q, tableInfo, err := parseQuery(ctx, args, m.state.ResultSet(), m.tableService)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
commandctrl.PostMsg(ctx, m.readController.RunQuery(q, tableInfo))
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *uiModule) uiFilter(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|
var filter string
|
|
|
|
if err := args.Bind(&filter); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
commandctrl.PostMsg(ctx, m.readController.Filter(filter))
|
|
return nil, nil
|
|
}
|
|
|
|
func moduleUI(
|
|
tableService *tables.Service,
|
|
state *controllers.State,
|
|
readController *controllers.TableReadController,
|
|
) (ucl.Module, controllers.CustomKeyBindingSource) {
|
|
m := &uiModule{
|
|
tableService: tableService,
|
|
state: state,
|
|
readController: readController,
|
|
ckb: &customKeyBinding{
|
|
bindings: map[string]tea.Cmd{},
|
|
keyBindings: map[string]string{},
|
|
},
|
|
}
|
|
|
|
return ucl.Module{
|
|
Name: "ui",
|
|
Builtins: map[string]ucl.BuiltinHandler{
|
|
"command": m.uiCommand,
|
|
"prompt": m.uiPrompt,
|
|
"prompt-table": m.uiPromptTable,
|
|
"confirm": m.uiConfirm,
|
|
"query": m.uiQuery,
|
|
"filter": m.uiFilter,
|
|
"bind": m.uiBind,
|
|
},
|
|
}, m.ckb
|
|
}
|
|
|
|
type customKeyBinding struct {
|
|
bindings map[string]tea.Cmd
|
|
keyBindings map[string]string
|
|
}
|
|
|
|
func (c *customKeyBinding) LookupBinding(theKey string) string {
|
|
return c.keyBindings[theKey]
|
|
}
|
|
|
|
func (c *customKeyBinding) CustomKeyCommand(key string) tea.Cmd {
|
|
bindingName, ok := c.keyBindings[key]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
binding, ok := c.bindings[bindingName]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
return binding
|
|
}
|
|
|
|
func (c *customKeyBinding) UnbindKey(key string) {
|
|
delete(c.keyBindings, key)
|
|
}
|
|
|
|
func (c *customKeyBinding) Rebind(bindingName string, newKey string) error {
|
|
c.keyBindings[newKey] = bindingName
|
|
return nil
|
|
}
|