package cmdpacks 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 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 }