- Converted Tamarin script language to Risor - Added a "find" and "merge" method to the result set script type. - Added the ability to copy the table of results to the pasteboard by pressing C - Added the -q flag, which will run a query and display the results as a CSV file on the command line - Upgraded Go to 1.21 in Github actions - Fix issue with missing limits - Added the '-where' switch to the mark - Added the 'marked' function to the query expression. - Added a sampled time and count on the right-side of the mode line - Added the 'M' key binding to toggle the marked items - Started working on tab completion for 'sa' and 'da' commands - Added count and sample time to the right-side of the mode line - Added Ctrl+V to the prompt to paste the text of the pasteboard with all whitespace characters trimmed - Fixed failing unit tests
147 lines
4 KiB
Go
147 lines
4 KiB
Go
package controllers
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/csv"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/lmika/dynamo-browse/internal/common/ui/events"
|
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/attrutils"
|
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/columns"
|
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services"
|
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/jobs"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type ExportController struct {
|
|
state *State
|
|
tableService TableReadService
|
|
jobController *JobsController
|
|
columns *ColumnsController
|
|
pasteboardProvider services.PasteboardProvider
|
|
}
|
|
|
|
func NewExportController(
|
|
state *State,
|
|
tableService TableReadService,
|
|
jobsController *JobsController,
|
|
columns *ColumnsController,
|
|
pasteboardProvider services.PasteboardProvider,
|
|
) *ExportController {
|
|
return &ExportController{state, tableService, jobsController, columns, pasteboardProvider}
|
|
}
|
|
|
|
func (c *ExportController) ExportCSV(filename string, opts ExportOptions) tea.Msg {
|
|
resultSet := c.state.ResultSet()
|
|
if resultSet == nil {
|
|
return events.Error(errors.New("no result set"))
|
|
}
|
|
|
|
return NewJob(c.jobController, fmt.Sprintf("Exporting to %v…", filename), func(ctx context.Context) (int, error) {
|
|
f, err := os.Create(filename)
|
|
if err != nil {
|
|
return 0, errors.Wrapf(err, "cannot export to '%v'", filename)
|
|
}
|
|
defer f.Close()
|
|
|
|
cw := csv.NewWriter(f)
|
|
defer cw.Flush()
|
|
|
|
columns := c.columns.Columns().VisibleColumns()
|
|
|
|
colNames := make([]string, len(columns))
|
|
for i, c := range columns {
|
|
colNames[i] = c.Name
|
|
}
|
|
if err := cw.Write(colNames); err != nil {
|
|
return 0, errors.Wrapf(err, "cannot export to '%v'", filename)
|
|
}
|
|
|
|
totalRows := 0
|
|
row := make([]string, len(columns))
|
|
for {
|
|
for _, item := range resultSet.Items() {
|
|
for i, col := range columns {
|
|
row[i], _ = attrutils.AttributeToString(col.Evaluator.EvaluateForItem(item))
|
|
}
|
|
if err := cw.Write(row); err != nil {
|
|
return 0, errors.Wrapf(err, "cannot export to '%v'", filename)
|
|
}
|
|
}
|
|
totalRows += len(resultSet.Items())
|
|
|
|
if !opts.AllResults || !resultSet.HasNextPage() {
|
|
break
|
|
}
|
|
|
|
jobs.PostUpdate(ctx, fmt.Sprintf("exported %d items", totalRows))
|
|
resultSet, err = c.tableService.NextPage(ctx, resultSet)
|
|
if err != nil {
|
|
return 0, errors.Wrapf(err, "cannot get next page while exporting to '%v'", filename)
|
|
}
|
|
}
|
|
|
|
return totalRows, nil
|
|
}).OnDone(func(rows int) tea.Msg {
|
|
return events.StatusMsg(applyToN("Exported ", rows, "item", "items", " to "+filename))
|
|
}).Submit()
|
|
}
|
|
|
|
func (c *ExportController) ExportCSVToClipboard() tea.Msg {
|
|
var bts bytes.Buffer
|
|
|
|
resultSet := c.state.ResultSet()
|
|
if resultSet == nil {
|
|
return errors.New("no result set")
|
|
}
|
|
|
|
if err := c.exportCSV(&bts, c.columns.Columns().VisibleColumns(), resultSet); err != nil {
|
|
return events.Error(err)
|
|
}
|
|
|
|
if err := c.pasteboardProvider.WriteText(bts.Bytes()); err != nil {
|
|
return events.Error(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// TODO: this really needs to be a service!
|
|
func (c *ExportController) ExportToWriter(w io.Writer, resultSet *models.ResultSet) error {
|
|
return c.exportCSV(w, columns.NewColumnsFromResultSet(resultSet).Columns, resultSet)
|
|
}
|
|
|
|
func (c *ExportController) exportCSV(w io.Writer, cols []columns.Column, resultSet *models.ResultSet) error {
|
|
cw := csv.NewWriter(w)
|
|
defer cw.Flush()
|
|
|
|
colNames := make([]string, len(cols))
|
|
for i, c := range cols {
|
|
colNames[i] = c.Name
|
|
}
|
|
if err := cw.Write(colNames); err != nil {
|
|
return errors.Wrap(err, "cannot export to clipboard")
|
|
}
|
|
|
|
row := make([]string, len(cols))
|
|
for _, item := range resultSet.Items() {
|
|
for i, col := range cols {
|
|
row[i], _ = attrutils.AttributeToString(col.Evaluator.EvaluateForItem(item))
|
|
}
|
|
if err := cw.Write(row); err != nil {
|
|
return errors.Wrap(err, "cannot export to clipboard")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type ExportOptions struct {
|
|
// AllResults returns all results from the table
|
|
AllResults bool
|
|
}
|