From 90ec88d360649b5ac38c6d483f863716440b8e5b Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Sat, 20 Aug 2022 10:41:32 +1000 Subject: [PATCH 1/2] issue-10: copy item to clipboard Added key binding to copy selected, or marked, items to clipboard. --- cmd/dynamo-browse/main.go | 8 ++- go.mod | 5 ++ go.sum | 36 ++++++++++ .../dynamo-browse/controllers/tableread.go | 72 ++++++++++++++++--- .../dynamo-browse/controllers/tablewrite.go | 21 ++---- internal/dynamo-browse/controllers/utils.go | 16 +++++ .../services/itemrenderer/service.go | 62 ++++++++++++++++ .../services/itemrenderer/stylerenderer.go | 11 +++ internal/dynamo-browse/ui/model.go | 14 +++- .../ui/teamodels/dynamoitemview/model.go | 67 ++++------------- .../ui/teamodels/styles/styles.go | 10 +++ 11 files changed, 236 insertions(+), 86 deletions(-) create mode 100644 internal/dynamo-browse/controllers/utils.go create mode 100644 internal/dynamo-browse/services/itemrenderer/service.go create mode 100644 internal/dynamo-browse/services/itemrenderer/stylerenderer.go diff --git a/cmd/dynamo-browse/main.go b/cmd/dynamo-browse/main.go index a5efea6..1f0f091 100644 --- a/cmd/dynamo-browse/main.go +++ b/cmd/dynamo-browse/main.go @@ -15,9 +15,11 @@ import ( "github.com/lmika/audax/internal/dynamo-browse/controllers" "github.com/lmika/audax/internal/dynamo-browse/providers/dynamo" "github.com/lmika/audax/internal/dynamo-browse/providers/workspacestore" + "github.com/lmika/audax/internal/dynamo-browse/services/itemrenderer" "github.com/lmika/audax/internal/dynamo-browse/services/tables" workspaces_service "github.com/lmika/audax/internal/dynamo-browse/services/workspaces" "github.com/lmika/audax/internal/dynamo-browse/ui" + "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/styles" "github.com/lmika/gopkgs/cli" "log" "net" @@ -66,18 +68,20 @@ func main() { dynamoClient = dynamodb.NewFromConfig(cfg) } + uiStyles := styles.DefaultStyles dynamoProvider := dynamo.NewProvider(dynamoClient) resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(ws) tableService := tables.NewService(dynamoProvider) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(uiStyles.ItemView.FieldType, uiStyles.ItemView.MetaInfo) state := controllers.NewState() - tableReadController := controllers.NewTableReadController(state, tableService, workspaceService, *flagTable) + tableReadController := controllers.NewTableReadController(state, tableService, workspaceService, itemRendererService, *flagTable) tableWriteController := controllers.NewTableWriteController(state, tableService, tableReadController) commandController := commandctrl.NewCommandController() - model := ui.NewModel(tableReadController, tableWriteController, commandController) + model := ui.NewModel(tableReadController, tableWriteController, itemRendererService, commandController) // Pre-determine if layout has dark background. This prevents calls for creating a list to hang. lipgloss.HasDarkBackground() diff --git a/go.mod b/go.mod index 19e0e51..333bc78 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.1 + golang.design/x/clipboard v0.6.2 ) require ( @@ -61,6 +62,10 @@ require ( github.com/sahilm/fuzzy v0.1.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect go.etcd.io/bbolt v1.3.6 // indirect + golang.design/x/clipboard v0.6.2 // indirect + golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect + golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect + golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 696b0f3..08d3f4f 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Sereal/Sereal v0.0.0-20220220040404-e0d1e550e879 h1:M5ptEKnqKqpFTKbe+p5zEf3ro1deJ6opUz5j3g3/ErQ= @@ -146,29 +147,64 @@ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMT github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +golang.design/x/clipboard v0.6.2 h1:a3Np4qfKnLWwfFJQhUWU3IDeRfmVuqWl+QPtP4CSYGw= +golang.design/x/clipboard v0.6.2/go.mod h1:kqBSweBP0/im4SZGGjLrppH0D400Hnfo5WbFKSNK8N4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 h1:3In5TnfvnuXTF/uflgpYxSCEGP2NdYT37KsPh3VjZYU= +golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65 h1:+rhAzEzT3f4JtomfC371qB+0Ola2caSKcY69NUBZrRQ= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= diff --git a/internal/dynamo-browse/controllers/tableread.go b/internal/dynamo-browse/controllers/tableread.go index 62a5777..0a4bac0 100644 --- a/internal/dynamo-browse/controllers/tableread.go +++ b/internal/dynamo-browse/controllers/tableread.go @@ -3,34 +3,47 @@ package controllers import ( "context" "encoding/csv" + "fmt" tea "github.com/charmbracelet/bubbletea" "github.com/lmika/audax/internal/common/ui/events" "github.com/lmika/audax/internal/dynamo-browse/models" "github.com/lmika/audax/internal/dynamo-browse/models/queryexpr" + "github.com/lmika/audax/internal/dynamo-browse/services/itemrenderer" "github.com/lmika/audax/internal/dynamo-browse/services/workspaces" "github.com/pkg/errors" + "golang.design/x/clipboard" "log" "os" + "strings" "sync" ) type TableReadController struct { - tableService TableReadService - workspaceService *workspaces.ViewSnapshotService - tableName string + tableService TableReadService + workspaceService *workspaces.ViewSnapshotService + itemRendererService *itemrenderer.Service + tableName string // state - mutex *sync.Mutex - state *State + mutex *sync.Mutex + state *State + clipboardInit bool } -func NewTableReadController(state *State, tableService TableReadService, workspaceService *workspaces.ViewSnapshotService, tableName string) *TableReadController { +func NewTableReadController( + state *State, + tableService TableReadService, + workspaceService *workspaces.ViewSnapshotService, + itemRendererService *itemrenderer.Service, + tableName string, +) *TableReadController { return &TableReadController{ - state: state, - tableService: tableService, - workspaceService: workspaceService, - tableName: tableName, - mutex: new(sync.Mutex), + state: state, + tableService: tableService, + workspaceService: workspaceService, + itemRendererService: itemRendererService, + tableName: tableName, + mutex: new(sync.Mutex), } } @@ -254,3 +267,40 @@ func (c *TableReadController) ViewBack() tea.Msg { tableInfo.Name, viewSnapshot.Query, viewSnapshot.Filter) return c.runQuery(tableInfo, viewSnapshot.Query, viewSnapshot.Filter, false) } + +func (c *TableReadController) CopyItemToClipboard(idx int) tea.Msg { + if err := c.initClipboard(); err != nil { + return events.Error(err) + } + + itemCount := 0 + c.state.withResultSet(func(resultSet *models.ResultSet) { + sb := new(strings.Builder) + _ = applyToMarkedItems(resultSet, idx, func(idx int, item models.Item) error { + if sb.Len() > 0 { + fmt.Fprintln(sb, "---") + } + c.itemRendererService.RenderItem(sb, resultSet.Items()[idx], resultSet, true) + itemCount += 1 + return nil + }) + clipboard.Write(clipboard.FmtText, []byte(sb.String())) + }) + + return events.SetStatus(applyToN("", itemCount, "item", "items", " copied to clipboard")) +} + +func (c *TableReadController) initClipboard() error { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.clipboardInit { + return nil + } + + if err := clipboard.Init(); err != nil { + return errors.Wrap(err, "unable to enable clipboard") + } + c.clipboardInit = true + return nil +} diff --git a/internal/dynamo-browse/controllers/tablewrite.go b/internal/dynamo-browse/controllers/tablewrite.go index c376d3f..14df92a 100644 --- a/internal/dynamo-browse/controllers/tablewrite.go +++ b/internal/dynamo-browse/controllers/tablewrite.go @@ -107,7 +107,7 @@ func (twc *TableWriteController) setStringValue(idx int, attr attrPath) tea.Msg Prompt: "string value: ", OnDone: func(value string) tea.Msg { if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error { - if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error { + if err := applyToMarkedItems(set, idx, func(idx int, item models.Item) error { if err := attr.setAt(item, &types.AttributeValueMemberS{Value: value}); err != nil { return err } @@ -126,25 +126,12 @@ func (twc *TableWriteController) setStringValue(idx int, attr attrPath) tea.Msg } } -func (twc *TableWriteController) applyToItems(rs *models.ResultSet, selectedIndex int, applyFn func(idx int, item models.Item) error) error { - if markedItems := rs.MarkedItems(); len(markedItems) > 0 { - for _, mi := range markedItems { - if err := applyFn(mi.Index, mi.Item); err != nil { - return err - } - } - return nil - } - - return applyFn(selectedIndex, rs.Items()[selectedIndex]) -} - func (twc *TableWriteController) setNumberValue(idx int, attr attrPath) tea.Msg { return events.PromptForInputMsg{ Prompt: "number value: ", OnDone: func(value string) tea.Msg { if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error { - if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error { + if err := applyToMarkedItems(set, idx, func(idx int, item models.Item) error { if err := attr.setAt(item, &types.AttributeValueMemberN{Value: value}); err != nil { return err } @@ -173,7 +160,7 @@ func (twc *TableWriteController) setBoolValue(idx int, attr attrPath) tea.Msg { } if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error { - if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error { + if err := applyToMarkedItems(set, idx, func(idx int, item models.Item) error { if err := attr.setAt(item, &types.AttributeValueMemberBOOL{Value: b}); err != nil { return err } @@ -194,7 +181,7 @@ func (twc *TableWriteController) setBoolValue(idx int, attr attrPath) tea.Msg { func (twc *TableWriteController) setNullValue(idx int, attr attrPath) tea.Msg { if err := twc.state.withResultSetReturningError(func(set *models.ResultSet) error { - if err := twc.applyToItems(set, idx, func(idx int, item models.Item) error { + if err := applyToMarkedItems(set, idx, func(idx int, item models.Item) error { if err := attr.setAt(item, &types.AttributeValueMemberNULL{Value: true}); err != nil { return err } diff --git a/internal/dynamo-browse/controllers/utils.go b/internal/dynamo-browse/controllers/utils.go new file mode 100644 index 0000000..9a09708 --- /dev/null +++ b/internal/dynamo-browse/controllers/utils.go @@ -0,0 +1,16 @@ +package controllers + +import "github.com/lmika/audax/internal/dynamo-browse/models" + +func applyToMarkedItems(rs *models.ResultSet, selectedIndex int, applyFn func(idx int, item models.Item) error) error { + if markedItems := rs.MarkedItems(); len(markedItems) > 0 { + for _, mi := range markedItems { + if err := applyFn(mi.Index, mi.Item); err != nil { + return err + } + } + return nil + } + + return applyFn(selectedIndex, rs.Items()[selectedIndex]) +} diff --git a/internal/dynamo-browse/services/itemrenderer/service.go b/internal/dynamo-browse/services/itemrenderer/service.go new file mode 100644 index 0000000..055da9a --- /dev/null +++ b/internal/dynamo-browse/services/itemrenderer/service.go @@ -0,0 +1,62 @@ +package itemrenderer + +import ( + "fmt" + "github.com/lmika/audax/internal/dynamo-browse/models" + "github.com/lmika/audax/internal/dynamo-browse/models/itemrender" + "io" + "text/tabwriter" +) + +type Service struct { + styles styleRenderer +} + +func NewService(fileTypeStyle StyleRenderer, metaInfoStyle StyleRenderer) *Service { + return &Service{ + styles: styleRenderer{ + fileTypeRenderer: fileTypeStyle, + metaInfoRenderer: metaInfoStyle, + }, + } +} + +func (s *Service) RenderItem(w io.Writer, item models.Item, resultSet *models.ResultSet, plainText bool) { + styles := s.styles + if plainText { + styles = styleRenderer{plainTextStyleRenderer{}, plainTextStyleRenderer{}} + } + + tabWriter := tabwriter.NewWriter(w, 0, 1, 1, ' ', 0) + + seenColumns := make(map[string]struct{}) + for _, colName := range resultSet.Columns() { + seenColumns[colName] = struct{}{} + if r := item.Renderer(colName); r != nil { + s.renderItem(tabWriter, "", colName, r, styles) + } + } + for k, _ := range item { + if _, seen := seenColumns[k]; !seen { + if r := item.Renderer(k); r != nil { + s.renderItem(tabWriter, "", k, r, styles) + } + } + } + tabWriter.Flush() +} + +func (m *Service) renderItem(w io.Writer, prefix string, name string, r itemrender.Renderer, sr styleRenderer) { + fmt.Fprintf(w, "%s%v\t%s\t%s%s\n", + prefix, name, sr.fileTypeRenderer.Render(r.TypeName()), r.StringValue(), sr.metaInfoRenderer.Render(r.MetaInfo())) + if subitems := r.SubItems(); len(subitems) > 0 { + for _, si := range subitems { + m.renderItem(w, prefix+" ", si.Key, si.Value, sr) + } + } +} + +type styleRenderer struct { + fileTypeRenderer StyleRenderer + metaInfoRenderer StyleRenderer +} diff --git a/internal/dynamo-browse/services/itemrenderer/stylerenderer.go b/internal/dynamo-browse/services/itemrenderer/stylerenderer.go new file mode 100644 index 0000000..04ea766 --- /dev/null +++ b/internal/dynamo-browse/services/itemrenderer/stylerenderer.go @@ -0,0 +1,11 @@ +package itemrenderer + +type StyleRenderer interface { + Render(str string) string +} + +type plainTextStyleRenderer struct{} + +func (plainTextStyleRenderer) Render(str string) string { + return str +} diff --git a/internal/dynamo-browse/ui/model.go b/internal/dynamo-browse/ui/model.go index 3cf2918..7d62e0a 100644 --- a/internal/dynamo-browse/ui/model.go +++ b/internal/dynamo-browse/ui/model.go @@ -6,6 +6,7 @@ import ( "github.com/lmika/audax/internal/common/ui/events" "github.com/lmika/audax/internal/dynamo-browse/controllers" "github.com/lmika/audax/internal/dynamo-browse/models" + "github.com/lmika/audax/internal/dynamo-browse/services/itemrenderer" "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/dialogprompt" "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/dynamoitemedit" "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/dynamoitemview" @@ -31,11 +32,16 @@ type Model struct { itemView *dynamoitemview.Model } -func NewModel(rc *controllers.TableReadController, wc *controllers.TableWriteController, cc *commandctrl.CommandController) Model { +func NewModel( + rc *controllers.TableReadController, + wc *controllers.TableWriteController, + itemRendererService *itemrenderer.Service, + cc *commandctrl.CommandController, +) Model { uiStyles := styles.DefaultStyles dtv := dynamotableview.New(uiStyles) - div := dynamoitemview.New(uiStyles) + div := dynamoitemview.New(itemRendererService, uiStyles) mainView := layout.NewVBox(layout.LastChildFixedAt(13), dtv, div) itemEdit := dynamoitemedit.NewModel(mainView) @@ -143,6 +149,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if idx := m.tableView.SelectedItemIndex(); idx >= 0 { return m, func() tea.Msg { return m.tableWriteController.ToggleMark(idx) } } + case "c": + if idx := m.tableView.SelectedItemIndex(); idx >= 0 { + return m, func() tea.Msg { return m.tableReadController.CopyItemToClipboard(idx) } + } case "R": return m, m.tableReadController.Rescan case "?": diff --git a/internal/dynamo-browse/ui/teamodels/dynamoitemview/model.go b/internal/dynamo-browse/ui/teamodels/dynamoitemview/model.go index f198cf0..1703a30 100644 --- a/internal/dynamo-browse/ui/teamodels/dynamoitemview/model.go +++ b/internal/dynamo-browse/ui/teamodels/dynamoitemview/model.go @@ -1,48 +1,34 @@ package dynamoitemview import ( - "fmt" - "github.com/lmika/audax/internal/dynamo-browse/models/itemrender" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/styles" - "io" - "strings" - "text/tabwriter" - "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/lmika/audax/internal/dynamo-browse/models" + "github.com/lmika/audax/internal/dynamo-browse/services/itemrenderer" "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/frame" "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout" -) - -var ( - activeHeaderStyle = lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#ffffff")). - Background(lipgloss.Color("#4479ff")) - - fieldTypeStyle = lipgloss.NewStyle(). - Foreground(lipgloss.AdaptiveColor{Light: "#2B800C", Dark: "#73C653"}) - metaInfoStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) + "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/styles" + "strings" ) type Model struct { - ready bool - frameTitle frame.FrameTitle - viewport viewport.Model - w, h int + ready bool + frameTitle frame.FrameTitle + viewport viewport.Model + itemRendererService *itemrenderer.Service + w, h int // model state currentResultSet *models.ResultSet selectedItem models.Item } -func New(uiStyles styles.Styles) *Model { +func New(itemRendererService *itemrenderer.Service, uiStyles styles.Styles) *Model { return &Model{ - frameTitle: frame.NewFrameTitle("Item", false, uiStyles.Frames), - viewport: viewport.New(100, 100), + itemRendererService: itemRendererService, + frameTitle: frame.NewFrameTitle("Item", false, uiStyles.Frames), + viewport: viewport.New(100, 100), } } @@ -89,35 +75,8 @@ func (m *Model) updateViewportToSelectedMessage() { } viewportContent := &strings.Builder{} - tabWriter := tabwriter.NewWriter(viewportContent, 0, 1, 1, ' ', 0) - - seenColumns := make(map[string]struct{}) - for _, colName := range m.currentResultSet.Columns() { - seenColumns[colName] = struct{}{} - if r := m.selectedItem.Renderer(colName); r != nil { - m.renderItem(tabWriter, "", colName, r) - } - } - for k, _ := range m.selectedItem { - if _, seen := seenColumns[k]; !seen { - if r := m.selectedItem.Renderer(k); r != nil { - m.renderItem(tabWriter, "", k, r) - } - } - } - - tabWriter.Flush() + m.itemRendererService.RenderItem(viewportContent, m.selectedItem, m.currentResultSet, false) m.viewport.Width = m.w m.viewport.Height = m.h - m.frameTitle.HeaderHeight() m.viewport.SetContent(viewportContent.String()) } - -func (m *Model) renderItem(w io.Writer, prefix string, name string, r itemrender.Renderer) { - fmt.Fprintf(w, "%s%v\t%s\t%s%s\n", - prefix, name, fieldTypeStyle.Render(r.TypeName()), r.StringValue(), metaInfoStyle.Render(r.MetaInfo())) - if subitems := r.SubItems(); len(subitems) > 0 { - for _, si := range subitems { - m.renderItem(w, prefix+" ", si.Key, si.Value) - } - } -} diff --git a/internal/dynamo-browse/ui/teamodels/styles/styles.go b/internal/dynamo-browse/ui/teamodels/styles/styles.go index 64f2460..29c25de 100644 --- a/internal/dynamo-browse/ui/teamodels/styles/styles.go +++ b/internal/dynamo-browse/ui/teamodels/styles/styles.go @@ -7,11 +7,21 @@ import ( ) type Styles struct { + ItemView ItemViewStyle Frames frame.Style StatusAndPrompt statusandprompt.Style } +type ItemViewStyle struct { + FieldType lipgloss.Style + MetaInfo lipgloss.Style +} + var DefaultStyles = Styles{ + ItemView: ItemViewStyle{ + FieldType: lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#2B800C", Dark: "#73C653"}), + MetaInfo: lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")), + }, Frames: frame.Style{ ActiveTitle: lipgloss.NewStyle(). Bold(true). From 9ab5da32e22ce3ea6a05cf32bcad4991764f3d6b Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Sat, 20 Aug 2022 10:47:56 +1000 Subject: [PATCH 2/2] issue-10: fixed unit tests --- .../controllers/tableread_test.go | 22 ++++++---- .../controllers/tablewrite_test.go | 42 +++++++++++-------- .../services/itemrenderer/stylerenderer.go | 4 ++ 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/internal/dynamo-browse/controllers/tableread_test.go b/internal/dynamo-browse/controllers/tableread_test.go index 9784040..5290118 100644 --- a/internal/dynamo-browse/controllers/tableread_test.go +++ b/internal/dynamo-browse/controllers/tableread_test.go @@ -8,6 +8,7 @@ import ( "github.com/lmika/audax/internal/dynamo-browse/controllers" "github.com/lmika/audax/internal/dynamo-browse/providers/dynamo" "github.com/lmika/audax/internal/dynamo-browse/providers/workspacestore" + "github.com/lmika/audax/internal/dynamo-browse/services/itemrenderer" "github.com/lmika/audax/internal/dynamo-browse/services/tables" workspaces_service "github.com/lmika/audax/internal/dynamo-browse/services/workspaces" "github.com/lmika/audax/test/testdynamo" @@ -22,12 +23,13 @@ func TestTableReadController_InitTable(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) provider := dynamo.NewProvider(client) service := tables.NewService(provider) t.Run("should prompt for table if no table name provided", func(t *testing.T) { - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "") event := readController.Init() @@ -35,7 +37,7 @@ func TestTableReadController_InitTable(t *testing.T) { }) t.Run("should scan table if table name provided", func(t *testing.T) { - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "") event := readController.Init() @@ -48,10 +50,11 @@ func TestTableReadController_ListTables(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) provider := dynamo.NewProvider(client) service := tables.NewService(provider) - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "") t.Run("returns a list of tables", func(t *testing.T) { event := readController.ListTables().(controllers.PromptForTableMsg) @@ -72,11 +75,12 @@ func TestTableReadController_Rescan(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) provider := dynamo.NewProvider(client) service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "bravo-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "bravo-table") t.Run("should perform a rescan", func(t *testing.T) { invokeCommand(t, readController.Init()) @@ -109,10 +113,11 @@ func TestTableReadController_ExportCSV(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) provider := dynamo.NewProvider(client) service := tables.NewService(provider) - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "bravo-table") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "bravo-table") t.Run("should export result set to CSV file", func(t *testing.T) { tempFile := tempFile(t) @@ -133,7 +138,7 @@ func TestTableReadController_ExportCSV(t *testing.T) { t.Run("should return error if result set is not set", func(t *testing.T) { tempFile := tempFile(t) - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "non-existant-table") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "non-existant-table") invokeCommandExpectingError(t, readController.Init()) invokeCommandExpectingError(t, readController.ExportCSV(tempFile)) @@ -147,10 +152,11 @@ func TestTableReadController_Query(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) provider := dynamo.NewProvider(client) service := tables.NewService(provider) - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "bravo-table") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "bravo-table") t.Run("should run scan with filter based on user query", func(t *testing.T) { tempFile := tempFile(t) @@ -170,7 +176,7 @@ func TestTableReadController_Query(t *testing.T) { t.Run("should return error if result set is not set", func(t *testing.T) { tempFile := tempFile(t) - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, "non-existant-table") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "non-existant-table") invokeCommandExpectingError(t, readController.Init()) invokeCommandExpectingError(t, readController.ExportCSV(tempFile)) diff --git a/internal/dynamo-browse/controllers/tablewrite_test.go b/internal/dynamo-browse/controllers/tablewrite_test.go index ff29b8c..e457a84 100644 --- a/internal/dynamo-browse/controllers/tablewrite_test.go +++ b/internal/dynamo-browse/controllers/tablewrite_test.go @@ -7,6 +7,7 @@ import ( "github.com/lmika/audax/internal/dynamo-browse/models" "github.com/lmika/audax/internal/dynamo-browse/providers/dynamo" "github.com/lmika/audax/internal/dynamo-browse/providers/workspacestore" + "github.com/lmika/audax/internal/dynamo-browse/services/itemrenderer" "github.com/lmika/audax/internal/dynamo-browse/services/tables" workspaces_service "github.com/lmika/audax/internal/dynamo-browse/services/workspaces" "github.com/lmika/audax/test/testdynamo" @@ -17,6 +18,7 @@ import ( func TestTableWriteController_NewItem(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) t.Run("should add an item with pk and sk set at the end of the result set", func(t *testing.T) { client := testdynamo.SetupTestTable(t, testData) @@ -25,7 +27,7 @@ func TestTableWriteController_NewItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -50,6 +52,7 @@ func TestTableWriteController_NewItem(t *testing.T) { func TestTableWriteController_SetAttributeValue(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) t.Run("should preserve the type of the field if unspecified", func(t *testing.T) { @@ -88,7 +91,7 @@ func TestTableWriteController_SetAttributeValue(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -108,7 +111,7 @@ func TestTableWriteController_SetAttributeValue(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -162,7 +165,7 @@ func TestTableWriteController_SetAttributeValue(t *testing.T) { for _, scenario := range scenarios { t.Run(fmt.Sprintf("should change the value of a field to type %v", scenario.attrType), func(t *testing.T) { state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -183,7 +186,7 @@ func TestTableWriteController_SetAttributeValue(t *testing.T) { t.Run(fmt.Sprintf("should change value of nested field to type %v", scenario.attrType), func(t *testing.T) { state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -215,13 +218,14 @@ func TestTableWriteController_DeleteAttribute(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) provider := dynamo.NewProvider(client) service := tables.NewService(provider) t.Run("should delete top level attribute", func(t *testing.T) { state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -237,7 +241,7 @@ func TestTableWriteController_DeleteAttribute(t *testing.T) { t.Run("should delete attribute of map", func(t *testing.T) { state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -260,6 +264,7 @@ func TestTableWriteController_DeleteAttribute(t *testing.T) { func TestTableWriteController_PutItem(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) t.Run("should put the selected item if dirty", func(t *testing.T) { client := testdynamo.SetupTestTable(t, testData) @@ -268,7 +273,7 @@ func TestTableWriteController_PutItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -295,7 +300,7 @@ func TestTableWriteController_PutItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -326,7 +331,7 @@ func TestTableWriteController_PutItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -342,6 +347,7 @@ func TestTableWriteController_PutItem(t *testing.T) { func TestTableWriteController_PutItems(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) t.Run("should put all dirty items if none are marked", func(t *testing.T) { client := testdynamo.SetupTestTable(t, testData) @@ -350,7 +356,7 @@ func TestTableWriteController_PutItems(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -378,7 +384,7 @@ func TestTableWriteController_PutItems(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -414,7 +420,7 @@ func TestTableWriteController_PutItems(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -447,6 +453,7 @@ func TestTableWriteController_PutItems(t *testing.T) { func TestTableWriteController_TouchItem(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) t.Run("should put the selected item if unmodified", func(t *testing.T) { client := testdynamo.SetupTestTable(t, testData) @@ -455,7 +462,7 @@ func TestTableWriteController_TouchItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -481,7 +488,7 @@ func TestTableWriteController_TouchItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -499,6 +506,7 @@ func TestTableWriteController_TouchItem(t *testing.T) { func TestTableWriteController_NoisyTouchItem(t *testing.T) { resultSetSnapshotStore := workspacestore.NewResultSetSnapshotStore(testWorkspace(t)) workspaceService := workspaces_service.NewService(resultSetSnapshotStore) + itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) t.Run("should delete and put the selected item if unmodified", func(t *testing.T) { client := testdynamo.SetupTestTable(t, testData) @@ -507,7 +515,7 @@ func TestTableWriteController_NoisyTouchItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -533,7 +541,7 @@ func TestTableWriteController_NoisyTouchItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") writeController := controllers.NewTableWriteController(state, service, readController) // Read the table diff --git a/internal/dynamo-browse/services/itemrenderer/stylerenderer.go b/internal/dynamo-browse/services/itemrenderer/stylerenderer.go index 04ea766..3092f68 100644 --- a/internal/dynamo-browse/services/itemrenderer/stylerenderer.go +++ b/internal/dynamo-browse/services/itemrenderer/stylerenderer.go @@ -4,6 +4,10 @@ type StyleRenderer interface { Render(str string) string } +func PlainTextRenderer() StyleRenderer { + return plainTextStyleRenderer{} +} + type plainTextStyleRenderer struct{} func (plainTextStyleRenderer) Render(str string) string {