ucl: Have started adding some of the psudo variables
This commit is contained in:
parent
18ffe85a56
commit
40f8dd76e2
2
go.mod
2
go.mod
|
@ -117,5 +117,5 @@ require (
|
|||
golang.org/x/text v0.9.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
ucl.lmika.dev v0.0.0-20250517115116-0f1ceba0902e // indirect
|
||||
ucl.lmika.dev v0.0.0-20250518033831-f79e91e26d78 // indirect
|
||||
)
|
||||
|
|
10
go.sum
10
go.sum
|
@ -444,3 +444,13 @@ ucl.lmika.dev v0.0.0-20250517003439-109be33d1495 h1:r46r+7T59Drm+in7TEWKCZfFYIM0
|
|||
ucl.lmika.dev v0.0.0-20250517003439-109be33d1495/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY=
|
||||
ucl.lmika.dev v0.0.0-20250517115116-0f1ceba0902e h1:CQ+qPqI5lYiiEM0tNAr4jS0iMz16bFqOui5mU3AHsCU=
|
||||
ucl.lmika.dev v0.0.0-20250517115116-0f1ceba0902e/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY=
|
||||
ucl.lmika.dev v0.0.0-20250517212052-51e35aa9a675 h1:kGKh3zj6lMzOrGAquFW7ROgx9/6nwJ8DXiSLtceRiak=
|
||||
ucl.lmika.dev v0.0.0-20250517212052-51e35aa9a675/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY=
|
||||
ucl.lmika.dev v0.0.0-20250517212757-33d04ba18db4 h1:rnietWu2B+NXLqKfo7jgf6r+srMwxFa5eizywkq4LFk=
|
||||
ucl.lmika.dev v0.0.0-20250517212757-33d04ba18db4/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY=
|
||||
ucl.lmika.dev v0.0.0-20250517213937-94aad417121d h1:CMcA8aQV6iiPK75EbHvoIVZhZmSggfrWNhK9BFm2aIg=
|
||||
ucl.lmika.dev v0.0.0-20250517213937-94aad417121d/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY=
|
||||
ucl.lmika.dev v0.0.0-20250518024533-f4be44fcbc94 h1:x3IRtT1jbedblimi2hesKGBihg243+wNOSvagCPR0KU=
|
||||
ucl.lmika.dev v0.0.0-20250518024533-f4be44fcbc94/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY=
|
||||
ucl.lmika.dev v0.0.0-20250518033831-f79e91e26d78 h1:lbOZUb6whYMLI4win5QL+eLSgqc3N9TtTgT8hTipNl8=
|
||||
ucl.lmika.dev v0.0.0-20250518033831-f79e91e26d78/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY=
|
||||
|
|
|
@ -45,12 +45,10 @@ func (rs *rsModule) rsNew(ctx context.Context, args ucl.CallArgs) (_ any, err er
|
|||
return nil, errors.New("no table specified")
|
||||
}
|
||||
|
||||
return ResultSetProxy{
|
||||
RS: &models.ResultSet{
|
||||
TableInfo: tableInfo,
|
||||
Created: time.Now(),
|
||||
},
|
||||
}, nil
|
||||
return newResultSetProxy(&models.ResultSet{
|
||||
TableInfo: tableInfo,
|
||||
Created: time.Now(),
|
||||
}), nil
|
||||
}
|
||||
|
||||
var rsQueryDoc = repl.Doc{
|
||||
|
@ -128,9 +126,7 @@ func (rs *rsModule) rsQuery(ctx context.Context, args ucl.CallArgs) (any, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ResultSetProxy{
|
||||
RS: newResultSet,
|
||||
}, nil
|
||||
return newResultSetProxy(newResultSet), nil
|
||||
}
|
||||
|
||||
func moduleRS(tableService *tables.Service, state *controllers.State) ucl.Module {
|
||||
|
|
|
@ -1,7 +1,180 @@
|
|||
package cmdpacks
|
||||
|
||||
import "github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
||||
"maps"
|
||||
"strconv"
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
||||
type ResultSetProxy struct {
|
||||
RS *models.ResultSet
|
||||
type proxyFields[T any] map[string]func(t T) ucl.Object
|
||||
|
||||
type simpleProxy[T comparable] struct {
|
||||
value T
|
||||
fields proxyFields[T]
|
||||
}
|
||||
|
||||
func (tp simpleProxy[T]) String() string {
|
||||
return fmt.Sprint(tp.value)
|
||||
}
|
||||
|
||||
func (tp simpleProxy[T]) Truthy() bool {
|
||||
var zeroT T
|
||||
return tp.value != zeroT
|
||||
}
|
||||
|
||||
func (tp simpleProxy[T]) Len() int {
|
||||
return len(tp.fields)
|
||||
}
|
||||
|
||||
func (tp simpleProxy[T]) Value(k string) ucl.Object {
|
||||
f, ok := tp.fields[k]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return f(tp.value)
|
||||
}
|
||||
|
||||
func (tp simpleProxy[T]) Each(fn func(k string, v ucl.Object) error) error {
|
||||
for key := range maps.Keys(tp.fields) {
|
||||
if err := fn(key, tp.Value(key)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type simpleProxyList[T comparable] struct {
|
||||
values []T
|
||||
converter func(T) ucl.Object
|
||||
}
|
||||
|
||||
func newSimpleProxyList[T comparable](values []T, converter func(T) ucl.Object) simpleProxyList[T] {
|
||||
return simpleProxyList[T]{values: values, converter: converter}
|
||||
}
|
||||
|
||||
func (tp simpleProxyList[T]) String() string {
|
||||
return fmt.Sprint(tp.values)
|
||||
}
|
||||
|
||||
func (tp simpleProxyList[T]) Truthy() bool {
|
||||
return len(tp.values) > 0
|
||||
}
|
||||
|
||||
func (tp simpleProxyList[T]) Len() int {
|
||||
return len(tp.values)
|
||||
}
|
||||
|
||||
func (tp simpleProxyList[T]) Index(k int) ucl.Object {
|
||||
return tp.converter(tp.values[k])
|
||||
}
|
||||
|
||||
func newResultSetProxy(rs *models.ResultSet) ucl.Object {
|
||||
return simpleProxy[*models.ResultSet]{value: rs, fields: resultSetProxyFields}
|
||||
}
|
||||
|
||||
var resultSetProxyFields = proxyFields[*models.ResultSet]{
|
||||
"Table": func(t *models.ResultSet) ucl.Object { return newTableProxy(t.TableInfo) },
|
||||
"Items": func(t *models.ResultSet) ucl.Object { return resultSetItemsProxy{t} },
|
||||
}
|
||||
|
||||
func newTableProxy(table *models.TableInfo) ucl.Object {
|
||||
return simpleProxy[*models.TableInfo]{value: table, fields: tableProxyFields}
|
||||
}
|
||||
|
||||
var tableProxyFields = proxyFields[*models.TableInfo]{
|
||||
"Name": func(t *models.TableInfo) ucl.Object { return ucl.StringObject(t.Name) },
|
||||
"Keys": func(t *models.TableInfo) ucl.Object { return newKeyAttributeProxy(t.Keys) },
|
||||
"DefinedAttributes": func(t *models.TableInfo) ucl.Object { return ucl.StringListObject(t.DefinedAttributes) },
|
||||
"GSIs": func(t *models.TableInfo) ucl.Object { return newSimpleProxyList(t.GSIs, newGSIProxy) },
|
||||
}
|
||||
|
||||
func newKeyAttributeProxy(keyAttrs models.KeyAttribute) ucl.Object {
|
||||
return simpleProxy[models.KeyAttribute]{value: keyAttrs, fields: keyAttributeProxyFields}
|
||||
}
|
||||
|
||||
var keyAttributeProxyFields = proxyFields[models.KeyAttribute]{
|
||||
"PartitionKey": func(t models.KeyAttribute) ucl.Object { return ucl.StringObject(t.PartitionKey) },
|
||||
"SortKey": func(t models.KeyAttribute) ucl.Object { return ucl.StringObject(t.SortKey) },
|
||||
}
|
||||
|
||||
func newGSIProxy(gsi models.TableGSI) ucl.Object {
|
||||
return simpleProxy[models.TableGSI]{value: gsi, fields: gsiProxyFields}
|
||||
}
|
||||
|
||||
var gsiProxyFields = proxyFields[models.TableGSI]{
|
||||
"Name": func(t models.TableGSI) ucl.Object { return ucl.StringObject(t.Name) },
|
||||
"Keys": func(t models.TableGSI) ucl.Object { return newKeyAttributeProxy(t.Keys) },
|
||||
}
|
||||
|
||||
type resultSetItemsProxy struct {
|
||||
resultSet *models.ResultSet
|
||||
}
|
||||
|
||||
func (ip resultSetItemsProxy) String() string {
|
||||
return "items"
|
||||
}
|
||||
|
||||
func (ip resultSetItemsProxy) Truthy() bool {
|
||||
return len(ip.resultSet.Items()) > 0
|
||||
}
|
||||
|
||||
func (tp resultSetItemsProxy) Len() int {
|
||||
return len(tp.resultSet.Items())
|
||||
}
|
||||
|
||||
func (tp resultSetItemsProxy) Index(k int) ucl.Object {
|
||||
return itemProxy{resultSet: tp.resultSet, idx: k, item: tp.resultSet.Items()[k]}
|
||||
}
|
||||
|
||||
type itemProxy struct {
|
||||
resultSet *models.ResultSet
|
||||
idx int
|
||||
item models.Item
|
||||
}
|
||||
|
||||
func (ip itemProxy) String() string {
|
||||
return "item"
|
||||
}
|
||||
|
||||
func (ip itemProxy) Truthy() bool {
|
||||
return len(ip.item) > 0
|
||||
}
|
||||
|
||||
func (tp itemProxy) Len() int {
|
||||
return len(tp.item)
|
||||
}
|
||||
|
||||
func (tp itemProxy) Value(k string) ucl.Object {
|
||||
f, ok := tp.item[k]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return convertAttributeValueToUCLObject(f)
|
||||
}
|
||||
|
||||
func (tp itemProxy) Each(fn func(k string, v ucl.Object) error) error {
|
||||
for key := range maps.Keys(tp.item) {
|
||||
if err := fn(key, tp.Value(key)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertAttributeValueToUCLObject(attrValue types.AttributeValue) ucl.Object {
|
||||
switch t := attrValue.(type) {
|
||||
case *types.AttributeValueMemberS:
|
||||
return ucl.StringObject(t.Value)
|
||||
case *types.AttributeValueMemberN:
|
||||
i, err := strconv.ParseInt(t.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return ucl.IntObject(i)
|
||||
}
|
||||
// TODO: the rest
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -4,28 +4,59 @@ import (
|
|||
"context"
|
||||
"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/models"
|
||||
"github.com/pkg/errors"
|
||||
"log"
|
||||
)
|
||||
|
||||
type tablePVar struct {
|
||||
state *controllers.State
|
||||
}
|
||||
|
||||
func (rs tablePVar) Get(ctx context.Context) (any, error) {
|
||||
return newTableProxy(rs.state.ResultSet().TableInfo), nil
|
||||
}
|
||||
|
||||
type resultSetPVar struct {
|
||||
state *controllers.State
|
||||
readController *controllers.TableReadController
|
||||
}
|
||||
|
||||
func (rs resultSetPVar) Get(ctx context.Context) (any, error) {
|
||||
return ResultSetProxy{rs.state.ResultSet()}, nil
|
||||
return newResultSetProxy(rs.state.ResultSet()), nil
|
||||
}
|
||||
|
||||
func (rs resultSetPVar) Set(ctx context.Context, value any) error {
|
||||
rsVal, ok := value.(ResultSetProxy)
|
||||
rsVal, ok := value.(simpleProxy[*models.ResultSet])
|
||||
if !ok {
|
||||
return errors.New("new value to @resultset is not a result set")
|
||||
}
|
||||
|
||||
log.Printf("type = %T", rsVal.RS)
|
||||
|
||||
msg := rs.readController.SetResultSet(rsVal.RS)
|
||||
msg := rs.readController.SetResultSet(rsVal.value)
|
||||
commandctrl.PostMsg(ctx, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
type itemPVar struct {
|
||||
state *controllers.State
|
||||
}
|
||||
|
||||
func (rs itemPVar) Get(ctx context.Context) (any, error) {
|
||||
selItem, ok := commandctrl.SelectedItemIndex(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New("no item selected")
|
||||
}
|
||||
return itemProxy{rs.state.ResultSet(), selItem, rs.state.ResultSet().Items()[selItem]}, nil
|
||||
}
|
||||
|
||||
func (rs itemPVar) Set(ctx context.Context, value any) error {
|
||||
rsVal, ok := value.(itemProxy)
|
||||
if !ok {
|
||||
return errors.New("new value to @item is not an item")
|
||||
}
|
||||
|
||||
if msg := commandctrl.SetSelectedItemIndex(ctx, rsVal.idx); msg != nil {
|
||||
commandctrl.PostMsg(ctx, msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -386,4 +386,6 @@ func (sc StandardCommands) ConfigureUCL(ucl *ucl.Inst) {
|
|||
// set-opt --> alias to opts:set
|
||||
|
||||
ucl.SetPseudoVar("resultset", resultSetPVar{sc.State, sc.ReadController})
|
||||
ucl.SetPseudoVar("table", tablePVar{sc.State})
|
||||
ucl.SetPseudoVar("item", itemPVar{sc.State})
|
||||
}
|
||||
|
|
|
@ -24,3 +24,12 @@ func SelectedItemIndex(ctx context.Context) (int, bool) {
|
|||
|
||||
return cmdCtl.uiStateProvider.SelectedItemIndex(), true
|
||||
}
|
||||
|
||||
func SetSelectedItemIndex(ctx context.Context, newIdx int) tea.Msg {
|
||||
cmdCtl, ok := ctx.Value(commandCtlKey).(*CommandController)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmdCtl.uiStateProvider.SetSelectedItemIndex(newIdx)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package commandctrl
|
|||
|
||||
import (
|
||||
"context"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services"
|
||||
)
|
||||
|
||||
|
@ -11,4 +12,5 @@ type IterProvider interface {
|
|||
|
||||
type UIStateProvider interface {
|
||||
SelectedItemIndex() int
|
||||
SetSelectedItemIndex(newIdx int) tea.Msg
|
||||
}
|
||||
|
|
|
@ -397,3 +397,7 @@ func (m *Model) promptToQuit() tea.Msg {
|
|||
func (m *Model) SelectedItemIndex() int {
|
||||
return m.tableView.SelectedItemIndex()
|
||||
}
|
||||
|
||||
func (m *Model) SetSelectedItemIndex(newIdx int) tea.Msg {
|
||||
return m.tableView.SetSelectedItemIndex(newIdx)
|
||||
}
|
||||
|
|
|
@ -208,6 +208,27 @@ func (m *Model) SelectedItemIndex() int {
|
|||
return selectedItem.itemIndex
|
||||
}
|
||||
|
||||
func (m *Model) SetSelectedItemIndex(newIdx int) tea.Msg {
|
||||
cursor := m.table.Cursor()
|
||||
switch {
|
||||
case newIdx <= 0:
|
||||
m.table.GoTop()
|
||||
case newIdx >= len(m.rows)-1:
|
||||
m.table.GoBottom()
|
||||
case newIdx < cursor:
|
||||
delta := cursor - newIdx
|
||||
for d := 0; d < delta; d++ {
|
||||
m.table.GoUp()
|
||||
}
|
||||
case newIdx > cursor:
|
||||
delta := newIdx - cursor
|
||||
for d := 0; d < delta; d++ {
|
||||
m.table.GoDown()
|
||||
}
|
||||
}
|
||||
return m.postSelectedItemChanged()
|
||||
}
|
||||
|
||||
func (m *Model) selectedItem() (itemTableRow, bool) {
|
||||
resultSet := m.resultSet
|
||||
|
||||
|
|
Loading…
Reference in a new issue