Added some quality of life changes
All checks were successful
ci / Build (push) Successful in 3m40s

- Fixed the 'M' bind to mark all/none items, rather than toggle
- Fixed panic which was raised when reqesting @item with an index beyond the length of resultset
- Added changing the table when calling @table with a table or string name
This commit is contained in:
Leon Mika 2025-10-29 22:15:26 +11:00
parent 85a4f0b5e9
commit c8b65f6b0a
12 changed files with 380 additions and 15 deletions

View file

@ -154,6 +154,40 @@ func TestModRS_Query(t *testing.T) {
}
}
func TestModRS_Filter(t *testing.T) {
tests := []struct {
descr string
cmd string
}{
{
descr: "returns filtered items 1",
cmd: `
rs = rs:scan -table service-test-data
rs = rs:filter $rs 'pk="abc"'
assert (len $rs) "expected len == 2"
assert (eq $rs.First.pk "abc") "expected First.pk == abc"
`,
},
//{
// descr: "returns filtered items 2",
// cmd: `
// rs = rs:scan -table service-test-data
// rs = rs:filter $rs 'pk="bbb"'
// assert (len $rs) "expected len == 1"
// assert (eq $rs.First.pk "bbb") "expected First.pk == bbb"
// `,
//},
}
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)
})
}
}
func TestModRS_First(t *testing.T) {
tests := []struct {
descr string

View file

@ -174,6 +174,31 @@ func (tp resultSetItemsProxy) Index(k int) ucl.Object {
return itemProxy{resultSet: tp.resultSet, idx: k, item: tp.resultSet.Items()[k]}
}
type resultSetMarkedItemsProxy struct {
resultSet *models.ResultSet
}
func (ip resultSetMarkedItemsProxy) String() string {
return fmt.Sprintf("MarkedItems(%v)", len(ip.resultSet.MarkedItems()))
}
func (ip resultSetMarkedItemsProxy) Truthy() bool {
return len(ip.resultSet.MarkedItems()) > 0
}
func (tp resultSetMarkedItemsProxy) Len() int {
return len(tp.resultSet.MarkedItems())
}
func (tp resultSetMarkedItemsProxy) Index(k int) ucl.Object {
markedItems := tp.resultSet.MarkedItems()
if k >= len(markedItems) {
return nil
}
actualItem := tp.resultSet.Items()[markedItems[k].Index]
return itemProxy{resultSet: tp.resultSet, idx: markedItems[k].Index, item: actualItem}
}
type itemProxy struct {
resultSet *models.ResultSet
idx int

View file

@ -2,20 +2,53 @@ package cmdpacks
import (
"context"
"github.com/pkg/errors"
"lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models"
"github.com/pkg/errors"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/tables"
)
type tablePVar struct {
state *controllers.State
state *controllers.State
tableService *tables.Service
readController *controllers.TableReadController
}
func (rs tablePVar) Get(ctx context.Context) (any, error) {
return newTableProxy(rs.state.ResultSet().TableInfo), nil
}
func (rs tablePVar) Set(ctx context.Context, value any) error {
scanNewTable := func(name string) error {
tableInfo, err := rs.tableService.Describe(ctx, name)
if err != nil {
return errors.Wrapf(err, "cannot describe %v", name)
}
resultSet, err := rs.tableService.Scan(ctx, tableInfo)
if resultSet != nil {
resultSet = rs.tableService.Filter(resultSet, rs.state.Filter())
}
msg := rs.readController.SetResultSet(resultSet)
commandctrl.PostMsg(ctx, msg)
return nil
}
tblVal, ok := value.(SimpleProxy[*models.TableInfo])
if ok {
return scanNewTable(tblVal.value.Name)
}
strVal, ok := value.(string)
if ok {
return scanNewTable(strVal)
}
return errors.New("new value to @table is not a table name")
}
type resultSetPVar struct {
state *controllers.State
readController *controllers.TableReadController
@ -36,6 +69,15 @@ func (rs resultSetPVar) Set(ctx context.Context, value any) error {
return nil
}
type markedSetPVar struct {
state *controllers.State
readController *controllers.TableReadController
}
func (rs markedSetPVar) Get(ctx context.Context) (any, error) {
return resultSetMarkedItemsProxy{rs.state.ResultSet()}, nil
}
type itemPVar struct {
state *controllers.State
}
@ -43,9 +85,14 @@ type itemPVar struct {
func (rs itemPVar) Get(ctx context.Context) (any, error) {
selItem, ok := commandctrl.SelectedItemIndex(ctx)
if !ok {
return nil, errors.New("no item selected")
return nil, nil
}
return itemProxy{rs.state.ResultSet(), selItem, rs.state.ResultSet().Items()[selItem]}, nil
rset := rs.state.ResultSet()
if selItem < 0 || selItem >= len(rs.state.ResultSet().Items()) {
return nil, nil
}
return itemProxy{rset, selItem, rset.Items()[selItem]}, nil
}
func (rs itemPVar) Set(ctx context.Context, value any) error {

View file

@ -0,0 +1,32 @@
package cmdpacks_test
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestPVars(t *testing.T) {
tests := []struct {
descr string
cmd string
}{
{
descr: "returns item on empty result set",
cmd: `
ui:query '"a"="1"' -table service-test-data
@item
`,
},
}
for _, tt := range tests {
t.Run(tt.descr, func(t *testing.T) {
svc := newService(t)
ctx := t.Context()
_, err := svc.CommandController.ExecuteAndWait(ctx, tt.cmd)
assert.NoError(t, err)
})
}
}

View file

@ -2,6 +2,7 @@ package cmdpacks
import (
"context"
tea "github.com/charmbracelet/bubbletea"
"github.com/pkg/errors"
"lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl"
@ -426,8 +427,9 @@ func (sc StandardCommands) ConfigureUCL(ucl *ucl.Inst) {
ucl.SetBuiltin("q", sc.cmdQuit)
ucl.SetPseudoVar("resultset", resultSetPVar{sc.State, sc.ReadController})
ucl.SetPseudoVar("table", tablePVar{sc.State})
ucl.SetPseudoVar("table", tablePVar{sc.State, sc.TableService, sc.ReadController})
ucl.SetPseudoVar("item", itemPVar{sc.State})
ucl.SetPseudoVar("marked", markedSetPVar{sc.State, sc.ReadController})
}
func (sc StandardCommands) RunPrelude(ctx context.Context, ucl *ucl.Inst) error {
@ -438,4 +440,13 @@ func (sc StandardCommands) RunPrelude(ctx context.Context, ucl *ucl.Inst) error
const uclPrelude = `
ui:command unmark { mark none }
ui:command set-opt { |n k| opt:set $n $k }
ui:bind "view.toggle-marked-items" "M" {
markedCount = len @marked
if (eq $markedCount (len @resultset)) {
mark none
} else {
mark all
}
}
`