ucl: integrated ucl with the command evaluator
This commit is contained in:
parent
e37b8099a3
commit
b2ddc62555
|
@ -187,6 +187,7 @@ func main() {
|
||||||
jobsController.SetMessageSender(p.Send)
|
jobsController.SetMessageSender(p.Send)
|
||||||
scriptController.Init()
|
scriptController.Init()
|
||||||
scriptController.SetMessageSender(p.Send)
|
scriptController.SetMessageSender(p.Send)
|
||||||
|
commandController.SetMessageSender(p.Send)
|
||||||
|
|
||||||
log.Println("launching")
|
log.Println("launching")
|
||||||
if err := p.Start(); err != nil {
|
if err := p.Start(); err != nil {
|
||||||
|
|
9
go.mod
9
go.mod
|
@ -5,7 +5,7 @@ go 1.22
|
||||||
toolchain go1.22.0
|
toolchain go1.22.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/participle/v2 v2.0.0-beta.5
|
github.com/alecthomas/participle/v2 v2.1.1
|
||||||
github.com/asdine/storm v2.1.2+incompatible
|
github.com/asdine/storm v2.1.2+incompatible
|
||||||
github.com/aws/aws-sdk-go-v2 v1.18.1
|
github.com/aws/aws-sdk-go-v2 v1.18.1
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.18.27
|
github.com/aws/aws-sdk-go-v2/config v1.18.27
|
||||||
|
@ -23,13 +23,13 @@ require (
|
||||||
github.com/cloudcmds/tamarin v1.0.0
|
github.com/cloudcmds/tamarin v1.0.0
|
||||||
github.com/lmika/events v0.0.0-20200906102219-a2269cd4394e
|
github.com/lmika/events v0.0.0-20200906102219-a2269cd4394e
|
||||||
github.com/lmika/go-bubble-table v0.2.2-0.20220616114432-6bbb2995e538
|
github.com/lmika/go-bubble-table v0.2.2-0.20220616114432-6bbb2995e538
|
||||||
github.com/lmika/gopkgs v0.0.0-20211210041137-0dc91e939890
|
github.com/lmika/gopkgs v0.0.0-20240408110817-a02f6fc67d1f
|
||||||
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe
|
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe
|
||||||
github.com/mattn/go-runewidth v0.0.14
|
github.com/mattn/go-runewidth v0.0.14
|
||||||
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70
|
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70
|
||||||
github.com/muesli/reflow v0.3.0
|
github.com/muesli/reflow v0.3.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.9.0
|
||||||
golang.design/x/clipboard v0.6.2
|
golang.design/x/clipboard v0.6.2
|
||||||
golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a
|
golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a
|
||||||
)
|
)
|
||||||
|
@ -101,7 +101,7 @@ require (
|
||||||
github.com/risor-io/risor v1.4.0 // indirect
|
github.com/risor-io/risor v1.4.0 // indirect
|
||||||
github.com/rivo/uniseg v0.4.2 // indirect
|
github.com/rivo/uniseg v0.4.2 // indirect
|
||||||
github.com/sahilm/fuzzy v0.1.0 // indirect
|
github.com/sahilm/fuzzy v0.1.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.0 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/tidwall/gjson v1.14.3 // indirect
|
github.com/tidwall/gjson v1.14.3 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
|
@ -117,4 +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-20240501110514-25594c80d273 // indirect
|
||||||
)
|
)
|
||||||
|
|
12
go.sum
12
go.sum
|
@ -7,6 +7,8 @@ github.com/Sereal/Sereal v0.0.0-20220220040404-e0d1e550e879/go.mod h1:D0JMgToj/W
|
||||||
github.com/alecthomas/assert/v2 v2.0.3 h1:WKqJODfOiQG0nEJKFKzDIG3E29CN2/4zR9XGJzKIkbg=
|
github.com/alecthomas/assert/v2 v2.0.3 h1:WKqJODfOiQG0nEJKFKzDIG3E29CN2/4zR9XGJzKIkbg=
|
||||||
github.com/alecthomas/participle/v2 v2.0.0-beta.5 h1:y6dsSYVb1G5eK6mgmy+BgI3Mw35a3WghArZ/Hbebrjo=
|
github.com/alecthomas/participle/v2 v2.0.0-beta.5 h1:y6dsSYVb1G5eK6mgmy+BgI3Mw35a3WghArZ/Hbebrjo=
|
||||||
github.com/alecthomas/participle/v2 v2.0.0-beta.5/go.mod h1:RC764t6n4L8D8ITAJv0qdokritYSNR3wV5cVwmIEaMM=
|
github.com/alecthomas/participle/v2 v2.0.0-beta.5/go.mod h1:RC764t6n4L8D8ITAJv0qdokritYSNR3wV5cVwmIEaMM=
|
||||||
|
github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8=
|
||||||
|
github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c=
|
||||||
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
||||||
github.com/anthonynsimon/bild v0.13.0 h1:mN3tMaNds1wBWi1BrJq0ipDBhpkooYfu7ZFSMhXt1C8=
|
github.com/anthonynsimon/bild v0.13.0 h1:mN3tMaNds1wBWi1BrJq0ipDBhpkooYfu7ZFSMhXt1C8=
|
||||||
github.com/anthonynsimon/bild v0.13.0/go.mod h1:tpzzp0aYkAsMi1zmfhimaDyX1xjn2OUc1AJZK/TF0AE=
|
github.com/anthonynsimon/bild v0.13.0/go.mod h1:tpzzp0aYkAsMi1zmfhimaDyX1xjn2OUc1AJZK/TF0AE=
|
||||||
|
@ -223,6 +225,8 @@ github.com/lmika/go-bubble-table v0.2.2-0.20220616114432-6bbb2995e538 h1:dtMPRNo
|
||||||
github.com/lmika/go-bubble-table v0.2.2-0.20220616114432-6bbb2995e538/go.mod h1:0RT1upgKZ6qZ6B1SqseE3wWsPjSQRv/G/HjpYK8jNsg=
|
github.com/lmika/go-bubble-table v0.2.2-0.20220616114432-6bbb2995e538/go.mod h1:0RT1upgKZ6qZ6B1SqseE3wWsPjSQRv/G/HjpYK8jNsg=
|
||||||
github.com/lmika/gopkgs v0.0.0-20211210041137-0dc91e939890 h1:mwl/exYV/WkBMeShqK7q+B2w2r+b0vP1TSA7clBn9kI=
|
github.com/lmika/gopkgs v0.0.0-20211210041137-0dc91e939890 h1:mwl/exYV/WkBMeShqK7q+B2w2r+b0vP1TSA7clBn9kI=
|
||||||
github.com/lmika/gopkgs v0.0.0-20211210041137-0dc91e939890/go.mod h1:FH6OJSvYcJ9xY8CGs9yGgR89kMCK1UimuUQ6kE5YuJQ=
|
github.com/lmika/gopkgs v0.0.0-20211210041137-0dc91e939890/go.mod h1:FH6OJSvYcJ9xY8CGs9yGgR89kMCK1UimuUQ6kE5YuJQ=
|
||||||
|
github.com/lmika/gopkgs v0.0.0-20240408110817-a02f6fc67d1f h1:tz68Lhc1oR15HVz69IGbtdukdH0x70kBDEvvj5pTXyE=
|
||||||
|
github.com/lmika/gopkgs v0.0.0-20240408110817-a02f6fc67d1f/go.mod h1:zHQvhjGXRro/Xp2C9dbC+ZUpE0gL4GYW75x1lk7hwzI=
|
||||||
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe h1:1UXS/6OFkbi6JrihPykmYO1VtsABB02QQ+YmYYzTY18=
|
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe h1:1UXS/6OFkbi6JrihPykmYO1VtsABB02QQ+YmYYzTY18=
|
||||||
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe/go.mod h1:qpdOkLougV5Yry4Px9f1w1pNMavcr6Z67VW5Ro+vW5I=
|
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe/go.mod h1:qpdOkLougV5Yry4Px9f1w1pNMavcr6Z67VW5Ro+vW5I=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
|
@ -288,6 +292,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
@ -300,6 +306,8 @@ github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gt
|
||||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
||||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
@ -418,3 +426,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
ucl.lmika.dev v0.0.0-20240427010304-6315afc54287 h1:llPHrjca54duvQx9PgMTFDhOW2VQiVvqV1CEHpO4AnY=
|
||||||
|
ucl.lmika.dev v0.0.0-20240427010304-6315afc54287/go.mod h1:T6V4jIUxlWvMTgn4J752VDHNA8iyVrEX6v98EvDj8G4=
|
||||||
|
ucl.lmika.dev v0.0.0-20240501110514-25594c80d273 h1:+JpKw02VTAcOjJw7Q6juun/9hk9ypNSdTRlf+E4M5Nw=
|
||||||
|
ucl.lmika.dev v0.0.0-20240501110514-25594c80d273/go.mod h1:T6V4jIUxlWvMTgn4J752VDHNA8iyVrEX6v98EvDj8G4=
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"ucl.lmika.dev/ucl"
|
||||||
|
|
||||||
"github.com/lmika/dynamo-browse/internal/common/ui/events"
|
"github.com/lmika/dynamo-browse/internal/common/ui/events"
|
||||||
"github.com/lmika/shellwords"
|
"github.com/lmika/shellwords"
|
||||||
|
@ -18,18 +19,25 @@ import (
|
||||||
const commandsCategory = "commands"
|
const commandsCategory = "commands"
|
||||||
|
|
||||||
type CommandController struct {
|
type CommandController struct {
|
||||||
|
uclInst *ucl.Inst
|
||||||
historyProvider IterProvider
|
historyProvider IterProvider
|
||||||
commandList *CommandList
|
commandList *CommandList
|
||||||
|
msgSender func(tea.Msg)
|
||||||
lookupExtensions []CommandLookupExtension
|
lookupExtensions []CommandLookupExtension
|
||||||
completionProvider CommandCompletionProvider
|
completionProvider CommandCompletionProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommandController(historyProvider IterProvider) *CommandController {
|
func NewCommandController(historyProvider IterProvider) *CommandController {
|
||||||
return &CommandController{
|
cc := &CommandController{
|
||||||
historyProvider: historyProvider,
|
historyProvider: historyProvider,
|
||||||
commandList: nil,
|
commandList: nil,
|
||||||
lookupExtensions: nil,
|
lookupExtensions: nil,
|
||||||
}
|
}
|
||||||
|
cc.uclInst = ucl.New(
|
||||||
|
ucl.WithOut(ucl.LineHandler(cc.printLine)),
|
||||||
|
ucl.WithMissingBuiltinHandler(cc.cmdInvoker),
|
||||||
|
)
|
||||||
|
return cc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandController) AddCommands(ctx *CommandList) {
|
func (c *CommandController) AddCommands(ctx *CommandList) {
|
||||||
|
@ -37,6 +45,10 @@ func (c *CommandController) AddCommands(ctx *CommandList) {
|
||||||
c.commandList = ctx
|
c.commandList = ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CommandController) SetMessageSender(msg func(tea.Msg)) {
|
||||||
|
c.msgSender = msg
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CommandController) AddCommandLookupExtension(ext CommandLookupExtension) {
|
func (c *CommandController) AddCommandLookupExtension(ext CommandLookupExtension) {
|
||||||
c.lookupExtensions = append(c.lookupExtensions, ext)
|
c.lookupExtensions = append(c.lookupExtensions, ext)
|
||||||
}
|
}
|
||||||
|
@ -83,29 +95,25 @@ func (c *CommandController) execute(ctx ExecContext, commandInput string) tea.Ms
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens := shellwords.Split(input)
|
res, err := c.uclInst.Eval(context.Background(), commandInput)
|
||||||
command := c.lookupCommand(tokens[0])
|
if err != nil {
|
||||||
if command == nil {
|
return events.Error(err)
|
||||||
return events.Error(errors.New("no such command: " + tokens[0]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return command(ctx, tokens[1:])
|
if teaMsg, ok := res.(teaMsgWrapper); ok {
|
||||||
|
return teaMsg.msg
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandController) Alias(commandName string, aliasArgs []string) Command {
|
func (c *CommandController) Alias(commandName string) Command {
|
||||||
return func(ctx ExecContext, args []string) tea.Msg {
|
return func(ctx ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
command := c.lookupCommand(commandName)
|
command := c.lookupCommand(commandName)
|
||||||
if command == nil {
|
if command == nil {
|
||||||
return events.Error(errors.New("no such command: " + commandName))
|
return events.Error(errors.New("no such command: " + commandName))
|
||||||
}
|
}
|
||||||
|
|
||||||
var allArgs []string
|
return command(ctx, args)
|
||||||
if len(aliasArgs) > 0 {
|
|
||||||
allArgs = append(append([]string{}, aliasArgs...), args...)
|
|
||||||
} else {
|
|
||||||
allArgs = args
|
|
||||||
}
|
|
||||||
return command(ctx, allArgs)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,3 +168,26 @@ func (c *CommandController) executeFile(file []byte, filename string) error {
|
||||||
}
|
}
|
||||||
return scnr.Err()
|
return scnr.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CommandController) cmdInvoker(ctx context.Context, name string, args ucl.CallArgs) (any, error) {
|
||||||
|
command := c.lookupCommand(name)
|
||||||
|
if command == nil {
|
||||||
|
return nil, errors.New("no such command: " + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
res := command(ExecContext{}, args)
|
||||||
|
if errMsg, isErrMsg := res.(events.ErrorMsg); isErrMsg {
|
||||||
|
return nil, errMsg
|
||||||
|
}
|
||||||
|
return teaMsgWrapper{res}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandController) printLine(s string) {
|
||||||
|
if c.msgSender != nil {
|
||||||
|
c.msgSender(events.StatusMsg(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type teaMsgWrapper struct {
|
||||||
|
msg tea.Msg
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
package commandctrl
|
package commandctrl
|
||||||
|
|
||||||
import tea "github.com/charmbracelet/bubbletea"
|
import (
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"ucl.lmika.dev/ucl"
|
||||||
|
)
|
||||||
|
|
||||||
type Command func(ctx ExecContext, args []string) tea.Msg
|
type Command func(ctx ExecContext, args ucl.CallArgs) tea.Msg
|
||||||
|
|
||||||
func NoArgCommand(cmd tea.Cmd) Command {
|
func NoArgCommand(cmd tea.Cmd) Command {
|
||||||
return func(ctx ExecContext, args []string) tea.Msg {
|
return func(ctx ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
return cmd()
|
return cmd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
"ucl.lmika.dev/ucl"
|
||||||
|
|
||||||
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"
|
||||||
|
@ -106,11 +107,19 @@ func (sc *ScriptController) LookupCommand(name string) commandctrl.Command {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(execCtx commandctrl.ExecContext, args []string) tea.Msg {
|
return func(execCtx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
errChan := sc.waitAndPrintScriptError()
|
errChan := sc.waitAndPrintScriptError()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
if err := cmd.Invoke(ctx, args, errChan); err != nil {
|
invokeArgs := make([]string, 0)
|
||||||
|
for args.NArgs() > 0 {
|
||||||
|
var s string
|
||||||
|
if err := args.Bind(&s); err == nil {
|
||||||
|
invokeArgs = append(invokeArgs, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Invoke(ctx, invokeArgs, errChan); err != nil {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -3,7 +3,7 @@ package ui
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"ucl.lmika.dev/ucl"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
@ -97,30 +97,32 @@ func NewModel(
|
||||||
cc.AddCommands(&commandctrl.CommandList{
|
cc.AddCommands(&commandctrl.CommandList{
|
||||||
Commands: map[string]commandctrl.Command{
|
Commands: map[string]commandctrl.Command{
|
||||||
"quit": commandctrl.NoArgCommand(tea.Quit),
|
"quit": commandctrl.NoArgCommand(tea.Quit),
|
||||||
"table": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"table": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
if len(args) == 0 {
|
var tableName string
|
||||||
return rc.ListTables(false)
|
if err := args.Bind(&tableName); err == nil {
|
||||||
} else {
|
return rc.ScanTable(tableName)
|
||||||
return rc.ScanTable(args[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return rc.ListTables(false)
|
||||||
},
|
},
|
||||||
"export": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"export": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
if len(args) == 0 {
|
var filename string
|
||||||
|
if err := args.Bind(&filename); err != nil {
|
||||||
return events.Error(errors.New("expected filename"))
|
return events.Error(errors.New("expected filename"))
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := controllers.ExportOptions{}
|
opts := controllers.ExportOptions{
|
||||||
if len(args) == 2 && args[0] == "-all" {
|
AllResults: args.HasSwitch("all"),
|
||||||
opts.AllResults = true
|
|
||||||
args = args[1:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return exportController.ExportCSV(args[0], opts)
|
return exportController.ExportCSV(filename, opts)
|
||||||
},
|
},
|
||||||
"mark": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"mark": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
var markOp = controllers.MarkOpMark
|
var markOp = controllers.MarkOpMark
|
||||||
if len(args) > 0 {
|
|
||||||
switch args[0] {
|
var markOpStr string
|
||||||
|
if err := args.Bind(&markOpStr); err == nil {
|
||||||
|
switch markOpStr {
|
||||||
case "all":
|
case "all":
|
||||||
markOp = controllers.MarkOpMark
|
markOp = controllers.MarkOpMark
|
||||||
case "none":
|
case "none":
|
||||||
|
@ -133,108 +135,121 @@ func NewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
var whereExpr = ""
|
var whereExpr = ""
|
||||||
if len(args) == 3 && args[1] == "-where" {
|
_ = args.BindSwitch("where", &whereExpr)
|
||||||
whereExpr = args[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc.Mark(markOp, whereExpr)
|
return rc.Mark(markOp, whereExpr)
|
||||||
},
|
},
|
||||||
"next-page": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"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()
|
return rc.NextPage()
|
||||||
},
|
},
|
||||||
"delete": commandctrl.NoArgCommand(wc.DeleteMarked),
|
"delete": commandctrl.NoArgCommand(wc.DeleteMarked),
|
||||||
|
|
||||||
// TEMP
|
// TEMP
|
||||||
"new-item": commandctrl.NoArgCommand(wc.NewItem),
|
"new-item": commandctrl.NoArgCommand(wc.NewItem),
|
||||||
"clone": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"clone": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
return wc.CloneItem(dtv.SelectedItemIndex())
|
return wc.CloneItem(dtv.SelectedItemIndex())
|
||||||
},
|
},
|
||||||
"set-attr": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"set-attr": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
if len(args) == 0 {
|
var fieldName string
|
||||||
|
if err := args.Bind(&fieldName); err != nil {
|
||||||
return events.Error(errors.New("expected field"))
|
return events.Error(errors.New("expected field"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemType = models.UnsetItemType
|
var itemType = models.UnsetItemType
|
||||||
if len(args) == 2 {
|
switch {
|
||||||
switch strings.ToUpper(args[0]) {
|
case args.HasSwitch("S"):
|
||||||
case "-S":
|
itemType = models.StringItemType
|
||||||
itemType = models.StringItemType
|
case args.HasSwitch("N"):
|
||||||
case "-N":
|
itemType = models.NumberItemType
|
||||||
itemType = models.NumberItemType
|
case args.HasSwitch("BOOL"):
|
||||||
case "-BOOL":
|
itemType = models.BoolItemType
|
||||||
itemType = models.BoolItemType
|
case args.HasSwitch("NULL"):
|
||||||
case "-NULL":
|
itemType = models.NullItemType
|
||||||
itemType = models.NullItemType
|
case args.HasSwitch("TO"):
|
||||||
case "-TO":
|
itemType = models.ExprValueItemType
|
||||||
itemType = models.ExprValueItemType
|
default:
|
||||||
default:
|
return events.Error(errors.New("unrecognised item type"))
|
||||||
return events.Error(errors.New("unrecognised item type"))
|
|
||||||
}
|
|
||||||
args = args[1:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wc.SetAttributeValue(dtv.SelectedItemIndex(), itemType, args[0])
|
return wc.SetAttributeValue(dtv.SelectedItemIndex(), itemType, fieldName)
|
||||||
},
|
},
|
||||||
"del-attr": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"del-attr": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
if len(args) == 0 {
|
var fieldName string
|
||||||
|
// TODO: support rest args
|
||||||
|
if err := args.Bind(&fieldName); err != nil {
|
||||||
return events.Error(errors.New("expected field"))
|
return events.Error(errors.New("expected field"))
|
||||||
}
|
}
|
||||||
return wc.DeleteAttribute(dtv.SelectedItemIndex(), args[0])
|
|
||||||
|
return wc.DeleteAttribute(dtv.SelectedItemIndex(), fieldName)
|
||||||
},
|
},
|
||||||
|
|
||||||
"put": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"put": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
return wc.PutItems()
|
return wc.PutItems()
|
||||||
},
|
},
|
||||||
"touch": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"touch": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
return wc.TouchItem(dtv.SelectedItemIndex())
|
return wc.TouchItem(dtv.SelectedItemIndex())
|
||||||
},
|
},
|
||||||
"noisy-touch": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"noisy-touch": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
return wc.NoisyTouchItem(dtv.SelectedItemIndex())
|
return wc.NoisyTouchItem(dtv.SelectedItemIndex())
|
||||||
},
|
},
|
||||||
|
|
||||||
"echo": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
/*
|
||||||
s := new(strings.Builder)
|
"echo": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
for _, arg := range args {
|
s := new(strings.Builder)
|
||||||
s.WriteString(arg)
|
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"))
|
||||||
}
|
}
|
||||||
return events.StatusMsg(s.String())
|
|
||||||
},
|
var value string
|
||||||
"set": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
if err := args.Bind(&value); err == nil {
|
||||||
switch len(args) {
|
return settingsController.SetSetting(name, value)
|
||||||
case 1:
|
|
||||||
return settingsController.SetSetting(args[0], "")
|
|
||||||
case 2:
|
|
||||||
return settingsController.SetSetting(args[0], args[1])
|
|
||||||
}
|
}
|
||||||
return events.Error(errors.New("expected: settingName [value]"))
|
|
||||||
|
return settingsController.SetSetting(name, "")
|
||||||
},
|
},
|
||||||
"rebind": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"rebind": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
if len(args) != 2 {
|
var bindingName, newKey string
|
||||||
|
if err := args.Bind(&bindingName, &newKey); err != nil {
|
||||||
return events.Error(errors.New("expected: bindingName newKey"))
|
return events.Error(errors.New("expected: bindingName newKey"))
|
||||||
}
|
}
|
||||||
return keyBindingController.Rebind(args[0], args[1], ctx.FromFile)
|
|
||||||
|
return keyBindingController.Rebind(bindingName, newKey, ctx.FromFile)
|
||||||
},
|
},
|
||||||
|
|
||||||
"run-script": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"run-script": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
if len(args) != 1 {
|
var name string
|
||||||
|
if err := args.Bind(&name); err != nil {
|
||||||
return events.Error(errors.New("expected: script name"))
|
return events.Error(errors.New("expected: script name"))
|
||||||
}
|
}
|
||||||
return scriptController.RunScript(args[0])
|
|
||||||
|
return scriptController.RunScript(name)
|
||||||
},
|
},
|
||||||
"load-script": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
|
"load-script": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg {
|
||||||
if len(args) != 1 {
|
var name string
|
||||||
|
if err := args.Bind(&name); err != nil {
|
||||||
return events.Error(errors.New("expected: script name"))
|
return events.Error(errors.New("expected: script name"))
|
||||||
}
|
}
|
||||||
return scriptController.LoadScript(args[0])
|
|
||||||
|
return scriptController.LoadScript(name)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Aliases
|
// Aliases
|
||||||
"unmark": cc.Alias("mark", []string{"none"}),
|
"sa": cc.Alias("set-attr"),
|
||||||
"sa": cc.Alias("set-attr", nil),
|
"da": cc.Alias("del-attr"),
|
||||||
"da": cc.Alias("del-attr", nil),
|
"np": cc.Alias("next-page"),
|
||||||
"np": cc.Alias("next-page", nil),
|
"w": cc.Alias("put"),
|
||||||
"w": cc.Alias("put", nil),
|
"q": cc.Alias("quit"),
|
||||||
"q": cc.Alias("quit", nil),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue