Started re-engineering the UCL command instance
This commit is contained in:
parent
94b58e2168
commit
17381f3d0b
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1,3 @@
|
||||||
debug.log
|
debug.log
|
||||||
|
.DS_store
|
||||||
|
.idea
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl/cmdpacks"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
@ -159,7 +160,11 @@ func main() {
|
||||||
keyBindingService := keybindings_service.NewService(keyBindings)
|
keyBindingService := keybindings_service.NewService(keyBindings)
|
||||||
keyBindingController := controllers.NewKeyBindingController(keyBindingService, scriptController)
|
keyBindingController := controllers.NewKeyBindingController(keyBindingService, scriptController)
|
||||||
|
|
||||||
commandController := commandctrl.NewCommandController(inputHistoryService)
|
commandController := commandctrl.NewCommandController(inputHistoryService,
|
||||||
|
cmdpacks.StandardCommands{
|
||||||
|
ReadController: tableReadController,
|
||||||
|
},
|
||||||
|
)
|
||||||
commandController.AddCommandLookupExtension(scriptController)
|
commandController.AddCommandLookupExtension(scriptController)
|
||||||
commandController.SetCommandCompletionProvider(columnsController)
|
commandController.SetCommandCompletionProvider(columnsController)
|
||||||
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -117,5 +117,5 @@ require (
|
||||||
golang.org/x/text v0.9.0 // indirect
|
golang.org/x/text v0.9.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
ucl.lmika.dev v0.0.0-20240504013531-0dc9fd3c3281 // indirect
|
ucl.lmika.dev v0.0.0-20250306030053-ad6d002a22e8 // indirect
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -436,3 +436,5 @@ ucl.lmika.dev v0.0.0-20240504001444-cf3a12bf0d4d h1:OqGmR0Y+OG6aFIOlXy2QwEHtuUNa
|
||||||
ucl.lmika.dev v0.0.0-20240504001444-cf3a12bf0d4d/go.mod h1:T6V4jIUxlWvMTgn4J752VDHNA8iyVrEX6v98EvDj8G4=
|
ucl.lmika.dev v0.0.0-20240504001444-cf3a12bf0d4d/go.mod h1:T6V4jIUxlWvMTgn4J752VDHNA8iyVrEX6v98EvDj8G4=
|
||||||
ucl.lmika.dev v0.0.0-20240504013531-0dc9fd3c3281 h1:/M7phiv/0XVp3wKkOxEnGQysf8+RS6NOaBQZyUEoSsA=
|
ucl.lmika.dev v0.0.0-20240504013531-0dc9fd3c3281 h1:/M7phiv/0XVp3wKkOxEnGQysf8+RS6NOaBQZyUEoSsA=
|
||||||
ucl.lmika.dev v0.0.0-20240504013531-0dc9fd3c3281/go.mod h1:T6V4jIUxlWvMTgn4J752VDHNA8iyVrEX6v98EvDj8G4=
|
ucl.lmika.dev v0.0.0-20240504013531-0dc9fd3c3281/go.mod h1:T6V4jIUxlWvMTgn4J752VDHNA8iyVrEX6v98EvDj8G4=
|
||||||
|
ucl.lmika.dev v0.0.0-20250306030053-ad6d002a22e8 h1:vWttdW8GJWcTUQeJFbQHqCHJDLFWQ9nccUTx/lW2v8s=
|
||||||
|
ucl.lmika.dev v0.0.0-20250306030053-ad6d002a22e8/go.mod h1:FMP2ncSu4UxfvB0iA2zlebwL+1UPCARdyYNOrmi86A4=
|
||||||
|
|
60
internal/common/ui/commandctrl/cmdpacks/stdcmds.go
Normal file
60
internal/common/ui/commandctrl/cmdpacks/stdcmds.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package cmdpacks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/controllers"
|
||||||
|
"ucl.lmika.dev/repl"
|
||||||
|
"ucl.lmika.dev/ucl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StandardCommands struct {
|
||||||
|
ReadController *controllers.TableReadController
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdQuitDoc = repl.Doc{
|
||||||
|
Brief: "Quits dynamo-browse",
|
||||||
|
Usage: "quit",
|
||||||
|
Detailed: `
|
||||||
|
This will quit dynamo-browse immediately, without prompting to apply
|
||||||
|
any changes.
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc StandardCommands) cmdQuit(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
commandctrl.PostMsg(ctx, tea.Quit)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdTableDoc = repl.Doc{
|
||||||
|
Brief: "Prompt for table to scan",
|
||||||
|
Usage: "table [NAME]",
|
||||||
|
Args: []repl.ArgDoc{
|
||||||
|
{Name: "name", Brief: "Name of the table to scan"},
|
||||||
|
},
|
||||||
|
Detailed: `
|
||||||
|
If called with an argument, it will scan the table with that name and
|
||||||
|
replace the current result set. If called without an argument, it will
|
||||||
|
prompt for a table to scan.
|
||||||
|
|
||||||
|
This command is intended only for interactive sessions and is not suitable
|
||||||
|
for scripting. The scan or table prompts will happen asynchronously.
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc StandardCommands) cmdTable(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
var tableName string
|
||||||
|
if err := args.Bind(&tableName); err == nil {
|
||||||
|
commandctrl.PostMsg(ctx, sc.ReadController.ScanTable(tableName))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
commandctrl.PostMsg(ctx, sc.ReadController.ListTables(false))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc StandardCommands) ConfigureUCL(ucl *ucl.Inst) {
|
||||||
|
ucl.SetBuiltin("quit", sc.cmdQuit)
|
||||||
|
ucl.SetBuiltin("table", sc.cmdTable)
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package commandctrl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"log"
|
"log"
|
||||||
|
@ -17,30 +18,42 @@ import (
|
||||||
|
|
||||||
const commandsCategory = "commands"
|
const commandsCategory = "commands"
|
||||||
|
|
||||||
|
type cmdMessage struct {
|
||||||
|
cmd string
|
||||||
|
}
|
||||||
|
|
||||||
type CommandController struct {
|
type CommandController struct {
|
||||||
uclInst *ucl.Inst
|
uclInst *ucl.Inst
|
||||||
historyProvider IterProvider
|
historyProvider IterProvider
|
||||||
commandList *CommandList
|
commandList *CommandList
|
||||||
lookupExtensions []CommandLookupExtension
|
lookupExtensions []CommandLookupExtension
|
||||||
completionProvider CommandCompletionProvider
|
completionProvider CommandCompletionProvider
|
||||||
|
cmdChan chan cmdMessage
|
||||||
msgChan chan tea.Msg
|
msgChan chan tea.Msg
|
||||||
interactive bool
|
interactive bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommandController(historyProvider IterProvider) *CommandController {
|
func NewCommandController(historyProvider IterProvider, pkgs ...CommandPack) *CommandController {
|
||||||
cc := &CommandController{
|
cc := &CommandController{
|
||||||
historyProvider: historyProvider,
|
historyProvider: historyProvider,
|
||||||
commandList: nil,
|
commandList: nil,
|
||||||
lookupExtensions: nil,
|
lookupExtensions: nil,
|
||||||
|
cmdChan: make(chan cmdMessage),
|
||||||
msgChan: make(chan tea.Msg),
|
msgChan: make(chan tea.Msg),
|
||||||
interactive: true,
|
interactive: true,
|
||||||
}
|
}
|
||||||
cc.uclInst = ucl.New(
|
cc.uclInst = ucl.New(
|
||||||
ucl.WithOut(ucl.LineHandler(cc.printLine)),
|
ucl.WithOut(ucl.LineHandler(cc.printLine)),
|
||||||
ucl.WithMissingBuiltinHandler(cc.cmdInvoker),
|
|
||||||
ucl.WithModule(builtins.OS()),
|
ucl.WithModule(builtins.OS()),
|
||||||
ucl.WithModule(builtins.FS(nil)),
|
ucl.WithModule(builtins.FS(nil)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
pkg.ConfigureUCL(cc.uclInst)
|
||||||
|
}
|
||||||
|
|
||||||
|
go cc.cmdLooper()
|
||||||
|
|
||||||
return cc
|
return cc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,17 +114,42 @@ func (c *CommandController) execute(ctx ExecContext, commandInput string) tea.Ms
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.uclInst.Eval(context.Background(), commandInput)
|
select {
|
||||||
if err != nil {
|
case c.cmdChan <- cmdMessage{cmd: input}:
|
||||||
return events.Error(err)
|
// good
|
||||||
|
default:
|
||||||
|
return events.Error(errors.New("command currently running"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if teaMsg, ok := res.(teaMsgWrapper); ok {
|
/*
|
||||||
return teaMsg.msg
|
res, err := c.uclInst.Eval(context.Background(), commandInput)
|
||||||
}
|
if err != nil {
|
||||||
|
return events.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if teaMsg, ok := res.(teaMsgWrapper); ok {
|
||||||
|
return teaMsg.msg
|
||||||
|
}
|
||||||
|
*/
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CommandController) cmdLooper() {
|
||||||
|
ctx := context.WithValue(context.Background(), commandCtlKey, c)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case cmdChan := <-c.cmdChan:
|
||||||
|
res, err := c.uclInst.Eval(ctx, cmdChan.cmd)
|
||||||
|
if err != nil {
|
||||||
|
c.postMessage(events.Error(err))
|
||||||
|
} else if res != nil {
|
||||||
|
c.postMessage(events.StatusMsg(fmt.Sprint(res)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CommandController) Alias(commandName string) Command {
|
func (c *CommandController) Alias(commandName string) Command {
|
||||||
return func(ctx ExecContext, args ucl.CallArgs) tea.Msg {
|
return func(ctx ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
command := c.lookupCommand(commandName)
|
command := c.lookupCommand(commandName)
|
||||||
|
@ -158,13 +196,13 @@ func (c *CommandController) ExecuteFile(filename string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandController) executeFile(file []byte, filename string) error {
|
func (c *CommandController) executeFile(file []byte, filename string) error {
|
||||||
msg := c.execute(ExecContext{FromFile: true}, string(file))
|
//msg := c.execute(ExecContext{FromFile: true}, string(file))
|
||||||
switch m := msg.(type) {
|
//switch m := msg.(type) {
|
||||||
case events.ErrorMsg:
|
//case events.ErrorMsg:
|
||||||
log.Printf("%v: error - %v", filename, m.Error())
|
// log.Printf("%v: error - %v", filename, m.Error())
|
||||||
case events.StatusMsg:
|
//case events.StatusMsg:
|
||||||
log.Printf("%v: %v", filename, string(m))
|
// log.Printf("%v: %v", filename, string(m))
|
||||||
}
|
//}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,6 +232,14 @@ func (c *CommandController) printLine(s string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CommandController) postMessage(msg tea.Msg) {
|
||||||
|
if c.msgChan == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.msgChan <- msg
|
||||||
|
}
|
||||||
|
|
||||||
type teaMsgWrapper struct {
|
type teaMsgWrapper struct {
|
||||||
msg tea.Msg
|
msg tea.Msg
|
||||||
}
|
}
|
||||||
|
|
17
internal/common/ui/commandctrl/ctx.go
Normal file
17
internal/common/ui/commandctrl/ctx.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package commandctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
)
|
||||||
|
|
||||||
|
type commandCtlKeyType struct{}
|
||||||
|
|
||||||
|
var commandCtlKey = commandCtlKeyType{}
|
||||||
|
|
||||||
|
func PostMsg(ctx context.Context, msg tea.Msg) {
|
||||||
|
cmdCtl, ok := ctx.Value(commandCtlKey).(*CommandController)
|
||||||
|
if ok {
|
||||||
|
cmdCtl.postMessage(msg)
|
||||||
|
}
|
||||||
|
}
|
7
internal/common/ui/commandctrl/packs.go
Normal file
7
internal/common/ui/commandctrl/packs.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package commandctrl
|
||||||
|
|
||||||
|
import "ucl.lmika.dev/ucl"
|
||||||
|
|
||||||
|
type CommandPack interface {
|
||||||
|
ConfigureUCL(ucl *ucl.Inst)
|
||||||
|
}
|
|
@ -1,16 +1,11 @@
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"ucl.lmika.dev/ucl"
|
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl"
|
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl"
|
||||||
"github.com/lmika/dynamo-browse/internal/common/ui/events"
|
"github.com/lmika/dynamo-browse/internal/common/ui/events"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/controllers"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/controllers"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/itemrenderer"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/itemrenderer"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/ui/keybindings"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/ui/keybindings"
|
||||||
|
@ -26,7 +21,8 @@ import (
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/ui/teamodels/tableselect"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/ui/teamodels/tableselect"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/ui/teamodels/utils"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/ui/teamodels/utils"
|
||||||
bus "github.com/lmika/events"
|
bus "github.com/lmika/events"
|
||||||
"github.com/pkg/errors"
|
"log"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -94,162 +90,164 @@ func NewModel(
|
||||||
dialogPrompt := dialogprompt.New(statusAndPrompt)
|
dialogPrompt := dialogprompt.New(statusAndPrompt)
|
||||||
tableSelect := tableselect.New(dialogPrompt, uiStyles)
|
tableSelect := tableselect.New(dialogPrompt, uiStyles)
|
||||||
|
|
||||||
cc.AddCommands(&commandctrl.CommandList{
|
/*
|
||||||
Commands: map[string]commandctrl.Command{
|
cc.AddCommands(&commandctrl.CommandList{
|
||||||
"quit": commandctrl.NoArgCommand(tea.Quit),
|
Commands: map[string]commandctrl.Command{
|
||||||
"table": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
"quit": commandctrl.NoArgCommand(tea.Quit),
|
||||||
var tableName string
|
"table": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
if err := args.Bind(&tableName); err == nil {
|
var tableName string
|
||||||
return rc.ScanTable(tableName)
|
if err := args.Bind(&tableName); err == nil {
|
||||||
}
|
return rc.ScanTable(tableName)
|
||||||
|
|
||||||
return rc.ListTables(false)
|
|
||||||
},
|
|
||||||
"export": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
var filename string
|
|
||||||
if err := args.Bind(&filename); err != nil {
|
|
||||||
return events.Error(errors.New("expected filename"))
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := controllers.ExportOptions{
|
|
||||||
AllResults: args.HasSwitch("all"),
|
|
||||||
}
|
|
||||||
|
|
||||||
return exportController.ExportCSV(filename, opts)
|
|
||||||
},
|
|
||||||
"mark": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
var markOp = controllers.MarkOpMark
|
|
||||||
|
|
||||||
var markOpStr string
|
|
||||||
if err := args.Bind(&markOpStr); err == nil {
|
|
||||||
switch markOpStr {
|
|
||||||
case "all":
|
|
||||||
markOp = controllers.MarkOpMark
|
|
||||||
case "none":
|
|
||||||
markOp = controllers.MarkOpUnmark
|
|
||||||
case "toggle":
|
|
||||||
markOp = controllers.MarkOpToggle
|
|
||||||
default:
|
|
||||||
return events.Error(errors.New("unrecognised mark operation"))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var whereExpr = ""
|
return rc.ListTables(false)
|
||||||
_ = args.BindSwitch("where", &whereExpr)
|
|
||||||
|
|
||||||
return rc.Mark(markOp, whereExpr)
|
|
||||||
},
|
|
||||||
"unmark": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
return rc.Mark(controllers.MarkOpUnmark, "")
|
|
||||||
},
|
|
||||||
"next-page": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
return rc.NextPage()
|
|
||||||
},
|
|
||||||
"delete": commandctrl.NoArgCommand(wc.DeleteMarked),
|
|
||||||
|
|
||||||
// TEMP
|
|
||||||
"new-item": commandctrl.NoArgCommand(wc.NewItem),
|
|
||||||
"clone": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
return wc.CloneItem(dtv.SelectedItemIndex())
|
|
||||||
},
|
|
||||||
"set-attr": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
var fieldName string
|
|
||||||
if err := args.Bind(&fieldName); err != nil {
|
|
||||||
return events.Error(errors.New("expected field"))
|
|
||||||
}
|
|
||||||
|
|
||||||
var itemType = models.UnsetItemType
|
|
||||||
switch {
|
|
||||||
case args.HasSwitch("S"):
|
|
||||||
itemType = models.StringItemType
|
|
||||||
case args.HasSwitch("N"):
|
|
||||||
itemType = models.NumberItemType
|
|
||||||
case args.HasSwitch("BOOL"):
|
|
||||||
itemType = models.BoolItemType
|
|
||||||
case args.HasSwitch("NULL"):
|
|
||||||
itemType = models.NullItemType
|
|
||||||
case args.HasSwitch("TO"):
|
|
||||||
itemType = models.ExprValueItemType
|
|
||||||
}
|
|
||||||
|
|
||||||
return wc.SetAttributeValue(dtv.SelectedItemIndex(), itemType, fieldName)
|
|
||||||
},
|
|
||||||
"del-attr": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
var fieldName string
|
|
||||||
// TODO: support rest args
|
|
||||||
if err := args.Bind(&fieldName); err != nil {
|
|
||||||
return events.Error(errors.New("expected field"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return wc.DeleteAttribute(dtv.SelectedItemIndex(), fieldName)
|
|
||||||
},
|
|
||||||
|
|
||||||
"put": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
return wc.PutItems()
|
|
||||||
},
|
|
||||||
"touch": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
return wc.TouchItem(dtv.SelectedItemIndex())
|
|
||||||
},
|
|
||||||
"noisy-touch": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
return wc.NoisyTouchItem(dtv.SelectedItemIndex())
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
"echo": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
s := new(strings.Builder)
|
|
||||||
for _, arg := range args {
|
|
||||||
s.WriteString(arg)
|
|
||||||
}
|
|
||||||
return events.StatusMsg(s.String())
|
|
||||||
},
|
},
|
||||||
*/
|
"export": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
"set-opt": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
var filename string
|
||||||
var name string
|
if err := args.Bind(&filename); err != nil {
|
||||||
if err := args.Bind(&name); err != nil {
|
return events.Error(errors.New("expected filename"))
|
||||||
return events.Error(errors.New("expected settingName"))
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var value string
|
opts := controllers.ExportOptions{
|
||||||
if err := args.Bind(&value); err == nil {
|
AllResults: args.HasSwitch("all"),
|
||||||
return settingsController.SetSetting(name, value)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return settingsController.SetSetting(name, "")
|
return exportController.ExportCSV(filename, opts)
|
||||||
|
},
|
||||||
|
"mark": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
var markOp = controllers.MarkOpMark
|
||||||
|
|
||||||
|
var markOpStr string
|
||||||
|
if err := args.Bind(&markOpStr); err == nil {
|
||||||
|
switch markOpStr {
|
||||||
|
case "all":
|
||||||
|
markOp = controllers.MarkOpMark
|
||||||
|
case "none":
|
||||||
|
markOp = controllers.MarkOpUnmark
|
||||||
|
case "toggle":
|
||||||
|
markOp = controllers.MarkOpToggle
|
||||||
|
default:
|
||||||
|
return events.Error(errors.New("unrecognised mark operation"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var whereExpr = ""
|
||||||
|
_ = args.BindSwitch("where", &whereExpr)
|
||||||
|
|
||||||
|
return rc.Mark(markOp, whereExpr)
|
||||||
|
},
|
||||||
|
"unmark": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
return rc.Mark(controllers.MarkOpUnmark, "")
|
||||||
|
},
|
||||||
|
"next-page": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
return rc.NextPage()
|
||||||
|
},
|
||||||
|
"delete": commandctrl.NoArgCommand(wc.DeleteMarked),
|
||||||
|
|
||||||
|
// TEMP
|
||||||
|
"new-item": commandctrl.NoArgCommand(wc.NewItem),
|
||||||
|
"clone": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
return wc.CloneItem(dtv.SelectedItemIndex())
|
||||||
|
},
|
||||||
|
"set-attr": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
var fieldName string
|
||||||
|
if err := args.Bind(&fieldName); err != nil {
|
||||||
|
return events.Error(errors.New("expected field"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var itemType = models.UnsetItemType
|
||||||
|
switch {
|
||||||
|
case args.HasSwitch("S"):
|
||||||
|
itemType = models.StringItemType
|
||||||
|
case args.HasSwitch("N"):
|
||||||
|
itemType = models.NumberItemType
|
||||||
|
case args.HasSwitch("BOOL"):
|
||||||
|
itemType = models.BoolItemType
|
||||||
|
case args.HasSwitch("NULL"):
|
||||||
|
itemType = models.NullItemType
|
||||||
|
case args.HasSwitch("TO"):
|
||||||
|
itemType = models.ExprValueItemType
|
||||||
|
}
|
||||||
|
|
||||||
|
return wc.SetAttributeValue(dtv.SelectedItemIndex(), itemType, fieldName)
|
||||||
|
},
|
||||||
|
"del-attr": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
var fieldName string
|
||||||
|
// TODO: support rest args
|
||||||
|
if err := args.Bind(&fieldName); err != nil {
|
||||||
|
return events.Error(errors.New("expected field"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return wc.DeleteAttribute(dtv.SelectedItemIndex(), fieldName)
|
||||||
|
},
|
||||||
|
|
||||||
|
"put": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
return wc.PutItems()
|
||||||
|
},
|
||||||
|
"touch": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
return wc.TouchItem(dtv.SelectedItemIndex())
|
||||||
|
},
|
||||||
|
"noisy-touch": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
return wc.NoisyTouchItem(dtv.SelectedItemIndex())
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"echo": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
s := new(strings.Builder)
|
||||||
|
for _, arg := range args {
|
||||||
|
s.WriteString(arg)
|
||||||
|
}
|
||||||
|
return events.StatusMsg(s.String())
|
||||||
|
},
|
||||||
|
"set-opt": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
var name string
|
||||||
|
if err := args.Bind(&name); err != nil {
|
||||||
|
return events.Error(errors.New("expected settingName"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var value string
|
||||||
|
if err := args.Bind(&value); err == nil {
|
||||||
|
return settingsController.SetSetting(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return settingsController.SetSetting(name, "")
|
||||||
|
},
|
||||||
|
"rebind": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
var bindingName, newKey string
|
||||||
|
if err := args.Bind(&bindingName, &newKey); err != nil {
|
||||||
|
return events.Error(errors.New("expected: bindingName newKey"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyBindingController.Rebind(bindingName, newKey, ctx.FromFile)
|
||||||
|
},
|
||||||
|
|
||||||
|
"run-script": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
var name string
|
||||||
|
if err := args.Bind(&name); err != nil {
|
||||||
|
return events.Error(errors.New("expected: script name"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return scriptController.RunScript(name)
|
||||||
|
},
|
||||||
|
"load-script": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
|
var name string
|
||||||
|
if err := args.Bind(&name); err != nil {
|
||||||
|
return events.Error(errors.New("expected: script name"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return scriptController.LoadScript(name)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Aliases
|
||||||
|
"sa": cc.Alias("set-attr"),
|
||||||
|
"da": cc.Alias("del-attr"),
|
||||||
|
"np": cc.Alias("next-page"),
|
||||||
|
"w": cc.Alias("put"),
|
||||||
|
"q": cc.Alias("quit"),
|
||||||
},
|
},
|
||||||
"rebind": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
})
|
||||||
var bindingName, newKey string
|
|
||||||
if err := args.Bind(&bindingName, &newKey); err != nil {
|
|
||||||
return events.Error(errors.New("expected: bindingName newKey"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyBindingController.Rebind(bindingName, newKey, ctx.FromFile)
|
*/
|
||||||
},
|
|
||||||
|
|
||||||
"run-script": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
var name string
|
|
||||||
if err := args.Bind(&name); err != nil {
|
|
||||||
return events.Error(errors.New("expected: script name"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return scriptController.RunScript(name)
|
|
||||||
},
|
|
||||||
"load-script": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
|
||||||
var name string
|
|
||||||
if err := args.Bind(&name); err != nil {
|
|
||||||
return events.Error(errors.New("expected: script name"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return scriptController.LoadScript(name)
|
|
||||||
},
|
|
||||||
|
|
||||||
// Aliases
|
|
||||||
"sa": cc.Alias("set-attr"),
|
|
||||||
"da": cc.Alias("del-attr"),
|
|
||||||
"np": cc.Alias("next-page"),
|
|
||||||
"w": cc.Alias("put"),
|
|
||||||
"q": cc.Alias("quit"),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
root := layout.FullScreen(tableSelect)
|
root := layout.FullScreen(tableSelect)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue