Skip to content

Commit

Permalink
Initial table template functions
Browse files Browse the repository at this point in the history
Resolves cli#3488
  • Loading branch information
heaths committed Aug 17, 2021
1 parent 6d45a92 commit 450bddb
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 2 deletions.
49 changes: 49 additions & 0 deletions pkg/export/table.go
@@ -0,0 +1,49 @@
package export

import (
"fmt"
"io"
"strings"
"text/tabwriter"
)

type tableFormatter struct {
current *table
writer io.Writer
}

type table struct {
tabwriter *tabwriter.Writer
}

// Initialize a new tabwriter.
func (f *tableFormatter) Table() string {
f.current = &table{
// Write directly to the same writer as template execution
// since writing to a buffer isn't getting flushed properly.
tabwriter: tabwriter.NewWriter(f.writer, 0, 1, 1, ' ', 0),
}
return ""
}

// Write a row of string fields. Formatting must be performed before passing to Row.
func (f *tableFormatter) Row(fields ...string) (string, error) {
if f.current == nil {
return "", fmt.Errorf("no current table. use '{{table}}' to start a table")
}
row := strings.Join(fields, "\t")
if _, err := f.current.tabwriter.Write([]byte(row + "\n")); err != nil {
return "", fmt.Errorf("failed to write row: %v", err)
}
return "", nil
}

// End the table and flush rows, adjusting for columns' widths using elastic tabstops.
func (f *tableFormatter) EndTable() (string, error) {
if f.current == nil {
return "", fmt.Errorf("no current table. use '{{table}}' to start a table")
}
f.current.tabwriter.Flush()
f.current = nil
return "", nil
}
26 changes: 26 additions & 0 deletions pkg/export/table_test.go
@@ -0,0 +1,26 @@
package export

import (
"strings"
"testing"
)

func Test_Row_withoutTable(t *testing.T) {
formatter := &tableFormatter{
current: nil,
writer: &strings.Builder{},
}
if _, err := formatter.Row("col"); err == nil {
t.Errorf("expected error, got nil")
}
}

func Test_EndTable_withoutTable(t *testing.T) {
formatter := &tableFormatter{
current: nil,
writer: &strings.Builder{},
}
if _, err := formatter.EndTable(); err == nil {
t.Errorf("expected error, got nil")
}
}
12 changes: 10 additions & 2 deletions pkg/export/template.go
Expand Up @@ -16,9 +16,13 @@ import (
"github.com/mgutz/ansi"
)

func parseTemplate(tpl string, colorEnabled bool) (*template.Template, error) {
func parseTemplate(w io.Writer, tpl string, colorEnabled bool) (*template.Template, error) {
now := time.Now()

tableFormatter := &tableFormatter{
current: nil,
writer: w,
}
templateFuncs := map[string]interface{}{
"color": templateColor,
"autocolor": templateColor,
Expand All @@ -41,6 +45,10 @@ func parseTemplate(tpl string, colorEnabled bool) (*template.Template, error) {
"ellipsis": templateEllipsis,
"join": templateJoin,
"pluck": templatePluck,

"table": tableFormatter.Table,
"row": tableFormatter.Row,
"endTable": tableFormatter.EndTable,
}

if !colorEnabled {
Expand All @@ -53,7 +61,7 @@ func parseTemplate(tpl string, colorEnabled bool) (*template.Template, error) {
}

func ExecuteTemplate(w io.Writer, input io.Reader, templateStr string, colorEnabled bool) error {
t, err := parseTemplate(templateStr, colorEnabled)
t, err := parseTemplate(w, templateStr, colorEnabled)
if err != nil {
return err
}
Expand Down
13 changes: 13 additions & 0 deletions pkg/export/template_test.go
Expand Up @@ -156,6 +156,19 @@ func Test_executeTemplate(t *testing.T) {
},
wantW: "bug\nfe...\nchore\n",
},
{
name: "table",
args: args{
json: strings.NewReader(heredoc.Doc(`[
{"number": 1, "title": "One"},
{"number": 20, "title": "Twenty"},
{"number": 3000, "title": "Three thousand"}
]`)),
template: `{{table}}{{range .}}{{row (.number | printf "#%v") .title}}{{end}}{{endTable}}`,
colorize: false,
},
wantW: "#1 One\n#20 Twenty\n#3000 Three thousand\n",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit 450bddb

Please sign in to comment.