backstack: an initial complete version of the backstack
This needs a lot of work, and a fair bit of refactoring.
This commit is contained in:
parent
721d3abe5e
commit
ec9ac34d26
|
@ -16,7 +16,7 @@ import (
|
|||
|
||||
type TableReadController struct {
|
||||
tableService TableReadService
|
||||
workspaceService *workspaces.Service
|
||||
workspaceService *workspaces.ViewSnapshotService
|
||||
tableName string
|
||||
|
||||
// state
|
||||
|
@ -26,7 +26,7 @@ type TableReadController struct {
|
|||
//filter string
|
||||
}
|
||||
|
||||
func NewTableReadController(state *State, tableService TableReadService, workspaceService *workspaces.Service, tableName string) *TableReadController {
|
||||
func NewTableReadController(state *State, tableService TableReadService, workspaceService *workspaces.ViewSnapshotService, tableName string) *TableReadController {
|
||||
return &TableReadController{
|
||||
state: state,
|
||||
tableService: tableService,
|
||||
|
@ -84,6 +84,11 @@ func (c *TableReadController) PromptForQuery() tea.Cmd {
|
|||
return events.PromptForInputMsg{
|
||||
Prompt: "query: ",
|
||||
OnDone: func(value string) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
return c.runQuery(c.state.ResultSet().TableInfo, value, "", true)
|
||||
}
|
||||
|
||||
/*
|
||||
if value == "" {
|
||||
return func() tea.Msg {
|
||||
resultSet := c.state.ResultSet()
|
||||
|
@ -103,27 +108,64 @@ func (c *TableReadController) PromptForQuery() tea.Cmd {
|
|||
return events.Error(err)
|
||||
}
|
||||
|
||||
if err := c.workspaceService.PushSnapshot(resultSet); err != nil {
|
||||
if err := c.workspaceService.PushSnapshot(resultSet, ""); err != nil {
|
||||
log.Printf("cannot push snapshot: %v", err)
|
||||
}
|
||||
return c.setResultSetAndFilter(newResultSet, "")
|
||||
})
|
||||
*/
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TableReadController) doIfNoneDirty(cmd tea.Cmd) tea.Cmd {
|
||||
func (c *TableReadController) runQuery(tableInfo *models.TableInfo, query, newFilter string, pushSnapshot bool) tea.Msg {
|
||||
if query == "" {
|
||||
newResultSet, err := c.tableService.ScanOrQuery(context.Background(), tableInfo, nil)
|
||||
if err != nil {
|
||||
return events.Error(err)
|
||||
}
|
||||
|
||||
if newFilter != "" {
|
||||
newResultSet = c.tableService.Filter(newResultSet, newFilter)
|
||||
}
|
||||
return c.setResultSetAndFilter(newResultSet, newFilter)
|
||||
}
|
||||
|
||||
expr, err := queryexpr.Parse(query)
|
||||
if err != nil {
|
||||
return events.SetError(err)
|
||||
}
|
||||
|
||||
return c.doIfNoneDirty(func() tea.Msg {
|
||||
resultSet := c.state.ResultSet()
|
||||
newResultSet, err := c.tableService.ScanOrQuery(context.Background(), tableInfo, expr)
|
||||
if err != nil {
|
||||
return events.Error(err)
|
||||
}
|
||||
|
||||
if pushSnapshot {
|
||||
if err := c.workspaceService.PushSnapshot(resultSet, c.state.Filter()); err != nil {
|
||||
log.Printf("cannot push snapshot: %v", err)
|
||||
}
|
||||
}
|
||||
if newFilter != "" {
|
||||
newResultSet = c.tableService.Filter(newResultSet, newFilter)
|
||||
}
|
||||
return c.setResultSetAndFilter(newResultSet, newFilter)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *TableReadController) doIfNoneDirty(cmd tea.Cmd) tea.Msg {
|
||||
var anyDirty = false
|
||||
for i := 0; i < len(c.state.ResultSet().Items()); i++ {
|
||||
anyDirty = anyDirty || c.state.ResultSet().IsDirty(i)
|
||||
}
|
||||
|
||||
if !anyDirty {
|
||||
return cmd
|
||||
return cmd()
|
||||
}
|
||||
|
||||
return func() tea.Msg {
|
||||
return events.PromptForInputMsg{
|
||||
Prompt: "reset modified items? ",
|
||||
OnDone: func(value string) tea.Cmd {
|
||||
|
@ -136,14 +178,14 @@ func (c *TableReadController) doIfNoneDirty(cmd tea.Cmd) tea.Cmd {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (c *TableReadController) Rescan() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
return c.doIfNoneDirty(func() tea.Msg {
|
||||
resultSet := c.state.ResultSet()
|
||||
return c.doScan(context.Background(), resultSet, resultSet.Query)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TableReadController) ExportCSV(filename string) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
|
@ -186,6 +228,7 @@ func (c *TableReadController) doScan(ctx context.Context, resultSet *models.Resu
|
|||
return events.Error(err)
|
||||
}
|
||||
|
||||
c.workspaceService.PushSnapshot(resultSet, c.state.Filter())
|
||||
newResultSet = c.tableService.Filter(newResultSet, c.state.Filter())
|
||||
|
||||
return c.setResultSetAndFilter(newResultSet, c.state.Filter())
|
||||
|
@ -214,6 +257,8 @@ func (c *TableReadController) Filter() tea.Cmd {
|
|||
OnDone: func(value string) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
resultSet := c.state.ResultSet()
|
||||
|
||||
c.workspaceService.PushSnapshot(resultSet, c.state.Filter())
|
||||
newResultSet := c.tableService.Filter(resultSet, value)
|
||||
|
||||
return c.setResultSetAndFilter(newResultSet, value)
|
||||
|
@ -222,3 +267,40 @@ func (c *TableReadController) Filter() tea.Cmd {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TableReadController) ViewBack() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
viewSnapshot, err := c.workspaceService.PopSnapshot()
|
||||
if err != nil {
|
||||
return events.Error(err)
|
||||
} else if viewSnapshot == nil {
|
||||
return events.StatusMsg("Backstack is empty")
|
||||
}
|
||||
|
||||
currentResultSet := c.state.ResultSet()
|
||||
|
||||
var currentQueryExpr string
|
||||
if currentResultSet.Query != nil {
|
||||
currentQueryExpr = currentResultSet.Query.String()
|
||||
}
|
||||
|
||||
if viewSnapshot.TableName == currentResultSet.TableInfo.Name && viewSnapshot.Query == currentQueryExpr {
|
||||
log.Printf("backstack: setting filter to '%v'", viewSnapshot.Filter)
|
||||
|
||||
newResultSet := c.tableService.Filter(currentResultSet, viewSnapshot.Filter)
|
||||
return c.setResultSetAndFilter(newResultSet, viewSnapshot.Filter)
|
||||
}
|
||||
|
||||
tableInfo := currentResultSet.TableInfo
|
||||
if viewSnapshot.TableName != currentResultSet.TableInfo.Name {
|
||||
tableInfo, err = c.tableService.Describe(context.Background(), viewSnapshot.TableName)
|
||||
if err != nil {
|
||||
return events.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("backstack: running query: table = '%v', query = '%v', filter = '%v'",
|
||||
tableInfo.Name, viewSnapshot.Query, viewSnapshot.Filter)
|
||||
return c.runQuery(tableInfo, viewSnapshot.Query, viewSnapshot.Filter, false)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
package serialisable
|
||||
|
||||
import (
|
||||
"github.com/lmika/audax/internal/dynamo-browse/models"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ResultSetSnapshot struct {
|
||||
ID int64 `storm:"id,increment"`
|
||||
BackLink int64 `storm:"index"`
|
||||
Time time.Time
|
||||
TableInfo *models.TableInfo
|
||||
Query Query
|
||||
Filter string
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
Expression string
|
||||
}
|
14
internal/dynamo-browse/models/serialisable/viewsnapshot.go
Normal file
14
internal/dynamo-browse/models/serialisable/viewsnapshot.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package serialisable
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type ViewSnapshot struct {
|
||||
ID int64 `storm:"id,increment"`
|
||||
BackLink int64 `storm:"index"`
|
||||
Time time.Time
|
||||
TableName string
|
||||
Query string
|
||||
Filter string
|
||||
}
|
|
@ -20,15 +20,22 @@ func NewResultSetSnapshotStore(ws *workspaces.Workspace) *ResultSetSnapshotStore
|
|||
}
|
||||
}
|
||||
|
||||
func (s *ResultSetSnapshotStore) Save(rs *serialisable.ResultSetSnapshot) error {
|
||||
func (s *ResultSetSnapshotStore) Save(rs *serialisable.ViewSnapshot) error {
|
||||
if err := s.ws.Save(rs); err != nil {
|
||||
return errors.Wrap(err, "cannot save result set")
|
||||
}
|
||||
log.Printf("saved result set")
|
||||
log.Printf("saved result set: table='%v', query='%v', filter='%v'", rs.TableName, rs.Query, rs.Filter)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ResultSetSnapshotStore) SetAsHead(resultSetID int64) error {
|
||||
if resultSetID == 0 {
|
||||
if err := s.ws.Delete("head", "id"); err != nil {
|
||||
return errors.Wrap(err, "cannot remove head")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.ws.Set("head", "id", resultSetID); err != nil {
|
||||
return errors.Wrap(err, "cannot set as head")
|
||||
}
|
||||
|
@ -36,13 +43,13 @@ func (s *ResultSetSnapshotStore) SetAsHead(resultSetID int64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *ResultSetSnapshotStore) Head() (*serialisable.ResultSetSnapshot, error) {
|
||||
func (s *ResultSetSnapshotStore) Head() (*serialisable.ViewSnapshot, error) {
|
||||
var headResultSetID int64
|
||||
if err := s.ws.Get("head", "id", &headResultSetID); err != nil && !errors.Is(err, storm.ErrNotFound) {
|
||||
return nil, errors.Wrap(err, "cannot get head")
|
||||
}
|
||||
|
||||
var rss serialisable.ResultSetSnapshot
|
||||
var rss serialisable.ViewSnapshot
|
||||
if err := s.ws.One("ID", headResultSetID, &rss); err != nil {
|
||||
if errors.Is(err, storm.ErrNotFound) {
|
||||
return nil, nil
|
||||
|
@ -53,3 +60,19 @@ func (s *ResultSetSnapshotStore) Head() (*serialisable.ResultSetSnapshot, error)
|
|||
|
||||
return &rss, nil
|
||||
}
|
||||
|
||||
func (s *ResultSetSnapshotStore) Remove(resultSetId int64) error {
|
||||
var rss serialisable.ViewSnapshot
|
||||
if err := s.ws.One("ID", resultSetId, &rss); err != nil {
|
||||
if errors.Is(err, storm.ErrNotFound) {
|
||||
return nil
|
||||
} else {
|
||||
return errors.Wrapf(err, "cannot get snapshot with ID %v", resultSetId)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.ws.DeleteStruct(&rss); err != nil {
|
||||
return errors.Wrap(err, "cannot delete snapshot")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,8 +2,9 @@ package workspaces
|
|||
|
||||
import "github.com/lmika/audax/internal/dynamo-browse/models/serialisable"
|
||||
|
||||
type ResultSetSnapshotStore interface {
|
||||
Save(rs *serialisable.ResultSetSnapshot) error
|
||||
type ViewSnapshotStore interface {
|
||||
Save(rs *serialisable.ViewSnapshot) error
|
||||
SetAsHead(resultSetId int64) error
|
||||
Head() (*serialisable.ResultSetSnapshot, error)
|
||||
Head() (*serialisable.ViewSnapshot, error)
|
||||
Remove(resultSetId int64) error
|
||||
}
|
||||
|
|
|
@ -7,24 +7,25 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
store ResultSetSnapshotStore
|
||||
type ViewSnapshotService struct {
|
||||
store ViewSnapshotStore
|
||||
}
|
||||
|
||||
func NewService(store ResultSetSnapshotStore) *Service {
|
||||
return &Service{
|
||||
func NewService(store ViewSnapshotStore) *ViewSnapshotService {
|
||||
return &ViewSnapshotService{
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) PushSnapshot(rs *models.ResultSet) error {
|
||||
newSnapshot := &serialisable.ResultSetSnapshot{
|
||||
func (s *ViewSnapshotService) PushSnapshot(rs *models.ResultSet, filter string) error {
|
||||
newSnapshot := &serialisable.ViewSnapshot{
|
||||
Time: time.Now(),
|
||||
TableInfo: rs.TableInfo,
|
||||
TableName: rs.TableInfo.Name,
|
||||
}
|
||||
if q := rs.Query; q != nil {
|
||||
newSnapshot.Query.Expression = q.String()
|
||||
newSnapshot.Query = q.String()
|
||||
}
|
||||
newSnapshot.Filter = filter
|
||||
|
||||
if head, err := s.store.Head(); head != nil {
|
||||
newSnapshot.BackLink = head.ID
|
||||
|
@ -41,3 +42,22 @@ func (s *Service) PushSnapshot(rs *models.ResultSet) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ViewSnapshotService) PopSnapshot() (*serialisable.ViewSnapshot, error) {
|
||||
vs, err := s.store.Head()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot get snapshot head")
|
||||
}
|
||||
if vs == nil {
|
||||
return vs, nil
|
||||
}
|
||||
|
||||
if err := s.store.SetAsHead(vs.BackLink); err != nil {
|
||||
return nil, errors.Wrap(err, "cannot set new head")
|
||||
}
|
||||
if err := s.store.Remove(vs.ID); err != nil {
|
||||
return nil, errors.Wrap(err, "cannot remove old ID")
|
||||
}
|
||||
|
||||
return vs, nil
|
||||
}
|
||||
|
|
|
@ -149,6 +149,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
return m, m.tableReadController.PromptForQuery()
|
||||
case "/":
|
||||
return m, m.tableReadController.Filter()
|
||||
case "backspace":
|
||||
return m, m.tableReadController.ViewBack()
|
||||
//case "e":
|
||||
// m.itemEdit.Visible()
|
||||
// return m, nil
|
||||
|
|
Loading…
Reference in a new issue