dynamo-browse/internal/common/ui/commandctrl/cmdpacks/modrs.go

124 lines
3 KiB
Go
Raw Normal View History

2025-05-17 01:11:04 +00:00
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,
},
}
}