Added rs:new and rs:query
This commit is contained in:
parent
cb908ec4eb
commit
6bf721873b
|
@ -162,9 +162,12 @@ func main() {
|
||||||
|
|
||||||
commandController := commandctrl.NewCommandController(inputHistoryService,
|
commandController := commandctrl.NewCommandController(inputHistoryService,
|
||||||
cmdpacks.StandardCommands{
|
cmdpacks.StandardCommands{
|
||||||
ReadController: tableReadController,
|
TableService: tableService,
|
||||||
WriteController: tableWriteController,
|
State: state,
|
||||||
ExportController: exportController,
|
ReadController: tableReadController,
|
||||||
|
WriteController: tableWriteController,
|
||||||
|
ExportController: exportController,
|
||||||
|
KeyBindingController: keyBindingController,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
commandController.AddCommandLookupExtension(scriptController)
|
commandController.AddCommandLookupExtension(scriptController)
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -1,8 +1,8 @@
|
||||||
module github.com/lmika/dynamo-browse
|
module github.com/lmika/dynamo-browse
|
||||||
|
|
||||||
go 1.22
|
go 1.24
|
||||||
|
|
||||||
toolchain go1.22.0
|
toolchain go1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/participle/v2 v2.1.1
|
github.com/alecthomas/participle/v2 v2.1.1
|
||||||
|
@ -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-20250306030053-ad6d002a22e8 // indirect
|
ucl.lmika.dev v0.0.0-20250517003439-109be33d1495 // indirect
|
||||||
)
|
)
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -438,3 +438,7 @@ ucl.lmika.dev v0.0.0-20240504013531-0dc9fd3c3281 h1:/M7phiv/0XVp3wKkOxEnGQysf8+R
|
||||||
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 h1:vWttdW8GJWcTUQeJFbQHqCHJDLFWQ9nccUTx/lW2v8s=
|
||||||
ucl.lmika.dev v0.0.0-20250306030053-ad6d002a22e8/go.mod h1:FMP2ncSu4UxfvB0iA2zlebwL+1UPCARdyYNOrmi86A4=
|
ucl.lmika.dev v0.0.0-20250306030053-ad6d002a22e8/go.mod h1:FMP2ncSu4UxfvB0iA2zlebwL+1UPCARdyYNOrmi86A4=
|
||||||
|
ucl.lmika.dev v0.0.0-20250515115457-27b6cc0b92e2 h1:cvguOoQ0HVgLKbHH17ZHvAUFht6HXApLi0o8JOdaaNU=
|
||||||
|
ucl.lmika.dev v0.0.0-20250515115457-27b6cc0b92e2/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY=
|
||||||
|
ucl.lmika.dev v0.0.0-20250517003439-109be33d1495 h1:r46r+7T59Drm+in7TEWKCZfFYIM0ZyZ26QjHAbj8Lto=
|
||||||
|
ucl.lmika.dev v0.0.0-20250517003439-109be33d1495/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY=
|
||||||
|
|
123
internal/common/ui/commandctrl/cmdpacks/modrs.go
Normal file
123
internal/common/ui/commandctrl/cmdpacks/modrs.go
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package cmdpacks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||||
|
"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/models/queryexpr"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/tables"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"ucl.lmika.dev/repl"
|
||||||
|
"ucl.lmika.dev/ucl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rsModule struct {
|
||||||
|
tableService *tables.Service
|
||||||
|
state *controllers.State
|
||||||
|
}
|
||||||
|
|
||||||
|
var rsNewDoc = repl.Doc{
|
||||||
|
Brief: "Creates a new, empty result set",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *rsModule) rsNew(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
return &ResultSetProxy{
|
||||||
|
RS: &models.ResultSet{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var rsQueryDoc = repl.Doc{
|
||||||
|
Brief: "Runs a query and returns the results as a result-set",
|
||||||
|
Usage: "QUERY [ARGS] [-table NAME]",
|
||||||
|
Args: []repl.ArgDoc{
|
||||||
|
{Name: "query", Brief: "Query expression to run"},
|
||||||
|
{Name: "args", Brief: "Hash of argument values to substitute into the query"},
|
||||||
|
{Name: "-table", Brief: "Optional table name to use for the query"},
|
||||||
|
},
|
||||||
|
Detailed: `
|
||||||
|
If no table is specified, then the value of @table will be used. If this is unavailable,
|
||||||
|
the command will return an error.
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *rsModule) rsQuery(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
var expr string
|
||||||
|
if err := args.Bind(&expr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
q, err := queryexpr.Parse(expr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.NArgs() > 0 {
|
||||||
|
var queryArgs ucl.Hashable
|
||||||
|
if err := args.Bind(&queryArgs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
queryNames := map[string]string{}
|
||||||
|
queryValues := map[string]types.AttributeValue{}
|
||||||
|
queryArgs.Each(func(k string, v ucl.Object) error {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
queryNames[k] = v.String()
|
||||||
|
|
||||||
|
switch v.(type) {
|
||||||
|
case ucl.StringObject:
|
||||||
|
queryValues[k] = &types.AttributeValueMemberS{Value: v.String()}
|
||||||
|
case ucl.IntObject:
|
||||||
|
queryValues[k] = &types.AttributeValueMemberN{Value: v.String()}
|
||||||
|
// TODO: other types
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
q = q.WithNameParams(queryNames).WithValueParams(queryValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tableInfo *models.TableInfo
|
||||||
|
if args.HasSwitch("table") {
|
||||||
|
var tblName string
|
||||||
|
if err := args.BindSwitch("table", &tblName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tableInfo, err = rs.tableService.Describe(ctx, tblName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else if currRs := rs.state.ResultSet(); currRs != nil && currRs.TableInfo != nil {
|
||||||
|
tableInfo = currRs.TableInfo
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("no table specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
newResultSet, err := rs.tableService.ScanOrQuery(context.Background(), tableInfo, q, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ResultSetProxy{
|
||||||
|
RS: newResultSet,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func moduleRS(tableService *tables.Service, state *controllers.State) ucl.Module {
|
||||||
|
m := &rsModule{
|
||||||
|
tableService: tableService,
|
||||||
|
state: state,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ucl.Module{
|
||||||
|
Name: "rs",
|
||||||
|
Builtins: map[string]ucl.BuiltinHandler{
|
||||||
|
"new": m.rsNew,
|
||||||
|
"query": m.rsQuery,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
80
internal/common/ui/commandctrl/cmdpacks/modrs_test.go
Normal file
80
internal/common/ui/commandctrl/cmdpacks/modrs_test.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package cmdpacks_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl/cmdpacks"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestModRS_New(t *testing.T) {
|
||||||
|
svc := newService(t)
|
||||||
|
|
||||||
|
rsProxy, err := svc.CommandController.ExecuteAndWait(t.Context(), `rs:new`)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.IsType(t, rsProxy, &cmdpacks.ResultSetProxy{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModRS_Query(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
descr string
|
||||||
|
cmd string
|
||||||
|
wantRows []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
descr: "query with pk 1",
|
||||||
|
cmd: `rs:query 'pk="abc"' -table service-test-data`,
|
||||||
|
wantRows: []int{0, 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
descr: "query with pk 2",
|
||||||
|
cmd: `rs:query 'pk="bbb"' -table service-test-data`,
|
||||||
|
wantRows: []int{2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
descr: "query with sk 1",
|
||||||
|
cmd: `rs:query 'sk="222"' -table service-test-data`,
|
||||||
|
wantRows: []int{1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
descr: "query with args 1",
|
||||||
|
cmd: `rs:query 'pk=$v' [v:'abc'] -table service-test-data`,
|
||||||
|
wantRows: []int{0, 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
descr: "query with args 2",
|
||||||
|
cmd: `rs:query ':k=$v' [k:'pk' v:'abc'] -table service-test-data`,
|
||||||
|
wantRows: []int{0, 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
descr: "query with args 3",
|
||||||
|
cmd: `rs:query ':k=$v' [k:'beta' v:1231] -table service-test-data`,
|
||||||
|
wantRows: []int{1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
descr: "query with args with no table set",
|
||||||
|
cmd: `rs:query ':k=$v' [k:'beta' v:1231]`,
|
||||||
|
wantRows: []int{1},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.descr, func(t *testing.T) {
|
||||||
|
svc := newService(t)
|
||||||
|
|
||||||
|
res, err := svc.CommandController.ExecuteAndWait(t.Context(), tt.cmd)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
rs := res.(*cmdpacks.ResultSetProxy).RS
|
||||||
|
assert.Len(t, rs.Items(), len(tt.wantRows))
|
||||||
|
|
||||||
|
for i, rowIndex := range tt.wantRows {
|
||||||
|
for key, want := range testData[0].Data[rowIndex] {
|
||||||
|
have, ok := rs.Items()[i].AttributeValueAsString(key)
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, fmt.Sprint(want), have)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
7
internal/common/ui/commandctrl/cmdpacks/proxy.go
Normal file
7
internal/common/ui/commandctrl/cmdpacks/proxy.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package cmdpacks
|
||||||
|
|
||||||
|
import "github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
||||||
|
|
||||||
|
type ResultSetProxy struct {
|
||||||
|
RS *models.ResultSet
|
||||||
|
}
|
|
@ -6,12 +6,15 @@ import (
|
||||||
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl"
|
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl"
|
||||||
"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/models"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/tables"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"ucl.lmika.dev/repl"
|
"ucl.lmika.dev/repl"
|
||||||
"ucl.lmika.dev/ucl"
|
"ucl.lmika.dev/ucl"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StandardCommands struct {
|
type StandardCommands struct {
|
||||||
|
TableService *tables.Service
|
||||||
|
State *controllers.State
|
||||||
ReadController *controllers.TableReadController
|
ReadController *controllers.TableReadController
|
||||||
WriteController *controllers.TableWriteController
|
WriteController *controllers.TableWriteController
|
||||||
ExportController *controllers.ExportController
|
ExportController *controllers.ExportController
|
||||||
|
@ -358,6 +361,12 @@ func (sc StandardCommands) cmdRebind(ctx context.Context, args ucl.CallArgs) (an
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc StandardCommands) InstOptions() []ucl.InstOption {
|
||||||
|
return []ucl.InstOption{
|
||||||
|
ucl.WithModule(moduleRS(sc.TableService, sc.State)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (sc StandardCommands) ConfigureUCL(ucl *ucl.Inst) {
|
func (sc StandardCommands) ConfigureUCL(ucl *ucl.Inst) {
|
||||||
ucl.SetBuiltin("quit", sc.cmdQuit)
|
ucl.SetBuiltin("quit", sc.cmdQuit)
|
||||||
ucl.SetBuiltin("table", sc.cmdTable)
|
ucl.SetBuiltin("table", sc.cmdTable)
|
||||||
|
|
143
internal/common/ui/commandctrl/cmdpacks/stdcmds_test.go
Normal file
143
internal/common/ui/commandctrl/cmdpacks/stdcmds_test.go
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
package cmdpacks_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl/cmdpacks"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/controllers"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/providers/dynamo"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/providers/inputhistorystore"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/providers/pasteboardprovider"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/providers/settingstore"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/providers/workspacestore"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/inputhistory"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/itemrenderer"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/jobs"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/tables"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/viewsnapshot"
|
||||||
|
"github.com/lmika/dynamo-browse/test/testdynamo"
|
||||||
|
"github.com/lmika/dynamo-browse/test/testworkspace"
|
||||||
|
bus "github.com/lmika/events"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStdCmds_Mark(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
descr string
|
||||||
|
cmd string
|
||||||
|
wantMarks []bool
|
||||||
|
}{
|
||||||
|
{descr: "mark default", cmd: "mark", wantMarks: []bool{true, true, true}},
|
||||||
|
{descr: "mark all", cmd: "mark all", wantMarks: []bool{true, true, true}},
|
||||||
|
{descr: "mark none", cmd: "mark none", wantMarks: []bool{false, false, false}},
|
||||||
|
{descr: "mark where", cmd: `mark -where 'sk="222"'`, wantMarks: []bool{false, true, false}},
|
||||||
|
{descr: "mark toggle", cmd: "mark ; mark toggle", wantMarks: []bool{false, false, false}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.descr, func(t *testing.T) {
|
||||||
|
svc := newService(t)
|
||||||
|
|
||||||
|
_, err := svc.CommandController.ExecuteAndWait(t.Context(), tt.cmd)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
for i, want := range tt.wantMarks {
|
||||||
|
assert.Equal(t, want, svc.State.ResultSet().Marked(i))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type services struct {
|
||||||
|
CommandController *commandctrl.CommandController
|
||||||
|
SelItemIndex int
|
||||||
|
|
||||||
|
State *controllers.State
|
||||||
|
}
|
||||||
|
|
||||||
|
func newService(t *testing.T) *services {
|
||||||
|
ws := testworkspace.New(t)
|
||||||
|
|
||||||
|
resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(ws)
|
||||||
|
settingStore := settingstore.New(ws)
|
||||||
|
inputHistoryStore := inputhistorystore.NewInputHistoryStore(ws)
|
||||||
|
|
||||||
|
workspaceService := viewsnapshot.NewService(resultSetSnapshotStore)
|
||||||
|
itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer())
|
||||||
|
inputHistoryService := inputhistory.New(inputHistoryStore)
|
||||||
|
|
||||||
|
client := testdynamo.SetupTestTable(t, testData)
|
||||||
|
|
||||||
|
provider := dynamo.NewProvider(client)
|
||||||
|
service := tables.NewService(provider, settingStore)
|
||||||
|
eventBus := bus.New()
|
||||||
|
|
||||||
|
state := controllers.NewState()
|
||||||
|
jobsController := controllers.NewJobsController(jobs.NewService(eventBus), eventBus, true)
|
||||||
|
readController := controllers.NewTableReadController(
|
||||||
|
state,
|
||||||
|
service,
|
||||||
|
workspaceService,
|
||||||
|
itemRendererService,
|
||||||
|
jobsController,
|
||||||
|
inputHistoryService,
|
||||||
|
eventBus,
|
||||||
|
pasteboardprovider.NilProvider{},
|
||||||
|
nil,
|
||||||
|
"service-test-data",
|
||||||
|
)
|
||||||
|
writeController := controllers.NewTableWriteController(state, service, jobsController, readController, settingStore)
|
||||||
|
settingsController := controllers.NewSettingsController(settingStore, eventBus)
|
||||||
|
columnsController := controllers.NewColumnsController(readController, eventBus)
|
||||||
|
exportController := controllers.NewExportController(state, service, jobsController, columnsController, pasteboardprovider.NilProvider{})
|
||||||
|
|
||||||
|
_ = settingsController
|
||||||
|
commandController := commandctrl.NewCommandController(inputHistoryService,
|
||||||
|
cmdpacks.StandardCommands{
|
||||||
|
State: state,
|
||||||
|
TableService: service,
|
||||||
|
ReadController: readController,
|
||||||
|
WriteController: writeController,
|
||||||
|
ExportController: exportController,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
s := &services{
|
||||||
|
State: state,
|
||||||
|
CommandController: commandController,
|
||||||
|
}
|
||||||
|
|
||||||
|
commandController.SetUIStateProvider(s)
|
||||||
|
readController.Init()
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *services) SelectedItemIndex() int {
|
||||||
|
return s.SelItemIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
var testData = []testdynamo.TestData{
|
||||||
|
{
|
||||||
|
TableName: "service-test-data",
|
||||||
|
Data: []map[string]interface{}{
|
||||||
|
{
|
||||||
|
"pk": "abc",
|
||||||
|
"sk": "111",
|
||||||
|
"alpha": "This is some value",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": "abc",
|
||||||
|
"sk": "222",
|
||||||
|
"alpha": "This is another some value",
|
||||||
|
"beta": 1231,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": "bbb",
|
||||||
|
"sk": "131",
|
||||||
|
"beta": 2468,
|
||||||
|
"gamma": "foobar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
|
@ -43,11 +43,17 @@ func NewCommandController(historyProvider IterProvider, pkgs ...CommandPack) *Co
|
||||||
msgChan: make(chan tea.Msg),
|
msgChan: make(chan tea.Msg),
|
||||||
interactive: true,
|
interactive: true,
|
||||||
}
|
}
|
||||||
cc.uclInst = ucl.New(
|
|
||||||
|
options := []ucl.InstOption{
|
||||||
ucl.WithOut(ucl.LineHandler(cc.printLine)),
|
ucl.WithOut(ucl.LineHandler(cc.printLine)),
|
||||||
ucl.WithModule(builtins.OS()),
|
ucl.WithModule(builtins.OS()),
|
||||||
ucl.WithModule(builtins.FS(nil)),
|
ucl.WithModule(builtins.FS(nil)),
|
||||||
)
|
}
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
options = append(options, pkg.InstOptions()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
cc.uclInst = ucl.New(options...)
|
||||||
|
|
||||||
for _, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
pkg.ConfigureUCL(cc.uclInst)
|
pkg.ConfigureUCL(cc.uclInst)
|
||||||
|
@ -126,26 +132,20 @@ func (c *CommandController) execute(ctx ExecContext, commandInput string) tea.Ms
|
||||||
return events.Error(errors.New("command currently running"))
|
return events.Error(errors.New("command currently running"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
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) ExecuteAndWait(ctx context.Context, commandInput string) (any, error) {
|
||||||
|
return c.uclInst.Eval(ctx, commandInput)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CommandController) cmdLooper() {
|
func (c *CommandController) cmdLooper() {
|
||||||
ctx := context.WithValue(context.Background(), commandCtlKey, c)
|
ctx := context.WithValue(context.Background(), commandCtlKey, c)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case cmdChan := <-c.cmdChan:
|
case cmdChan := <-c.cmdChan:
|
||||||
res, err := c.uclInst.Eval(ctx, cmdChan.cmd)
|
res, err := c.ExecuteAndWait(ctx, cmdChan.cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.postMessage(events.Error(err))
|
c.postMessage(events.Error(err))
|
||||||
} else if res != nil {
|
} else if res != nil {
|
||||||
|
|
|
@ -3,5 +3,6 @@ package commandctrl
|
||||||
import "ucl.lmika.dev/ucl"
|
import "ucl.lmika.dev/ucl"
|
||||||
|
|
||||||
type CommandPack interface {
|
type CommandPack interface {
|
||||||
|
InstOptions() []ucl.InstOption
|
||||||
ConfigureUCL(ucl *ucl.Inst)
|
ConfigureUCL(ucl *ucl.Inst)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type exprValue interface {
|
type exprValue interface {
|
||||||
|
@ -62,6 +63,14 @@ func newExprValueFromAttributeValue(ev types.AttributeValue) (exprValue, error)
|
||||||
case *types.AttributeValueMemberS:
|
case *types.AttributeValueMemberS:
|
||||||
return stringExprValue(xVal.Value), nil
|
return stringExprValue(xVal.Value), nil
|
||||||
case *types.AttributeValueMemberN:
|
case *types.AttributeValueMemberN:
|
||||||
|
if !strings.Contains(xVal.Value, ".") {
|
||||||
|
iVal, err := strconv.ParseInt(xVal.Value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return int64ExprValue(iVal), nil
|
||||||
|
}
|
||||||
|
|
||||||
xNumVal, _, err := big.ParseFloat(xVal.Value, 10, 63, big.ToNearestEven)
|
xNumVal, _, err := big.ParseFloat(xVal.Value, 10, 63, big.ToNearestEven)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -139,6 +148,32 @@ func (s int64ExprValue) typeName() string {
|
||||||
return "N"
|
return "N"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type bigIntExprValue struct {
|
||||||
|
num *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i bigIntExprValue) asGoValue() any {
|
||||||
|
return i.num
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i bigIntExprValue) asAttributeValue() types.AttributeValue {
|
||||||
|
return &types.AttributeValueMemberN{Value: i.num.String()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i bigIntExprValue) asInt() int64 {
|
||||||
|
return i.num.Int64()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i bigIntExprValue) asBigFloat() *big.Float {
|
||||||
|
var f big.Float
|
||||||
|
f.SetInt64(i.num.Int64())
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s bigIntExprValue) typeName() string {
|
||||||
|
return "N"
|
||||||
|
}
|
||||||
|
|
||||||
type bigNumExprValue struct {
|
type bigNumExprValue struct {
|
||||||
num *big.Float
|
num *big.Float
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue