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" "time" "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", Usage: "[-table NAME]", Detailed: ` The result set assumes the details of the current table. If no table is specified, the command will return an error. `, } func (rs *rsModule) rsNew(ctx context.Context, args ucl.CallArgs) (_ any, err error) { 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") } return ResultSetProxy{ RS: &models.ResultSet{ TableInfo: tableInfo, Created: time.Now(), }, }, 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, }, } }