feat: port table-printers from influxdb (#145)
This commit is contained in:
parent
1183d3780b
commit
fb2d19c884
2
go.mod
2
go.mod
@ -7,12 +7,14 @@ require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/MakeNowJust/heredoc/v2 v2.0.1
|
||||
github.com/daixiang0/gci v0.2.8
|
||||
github.com/fatih/color v1.9.0
|
||||
github.com/fujiwara/shapeio v1.0.0
|
||||
github.com/gocarina/gocsv v0.0.0-20210408192840-02d7211d929d
|
||||
github.com/golang/mock v1.5.0
|
||||
github.com/google/go-cmp v0.5.5
|
||||
github.com/google/go-jsonnet v0.17.0
|
||||
github.com/mattn/go-isatty v0.0.13
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/urfave/cli/v2 v2.3.0
|
||||
golang.org/x/text v0.3.3
|
||||
|
5
go.sum
5
go.sum
@ -15,6 +15,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fujiwara/shapeio v1.0.0 h1:xG5D9oNqCSUUbryZ/jQV3cqe1v2suEjwPIcEg1gKM8M=
|
||||
github.com/fujiwara/shapeio v1.0.0/go.mod h1:LmEmu6L/8jetyj1oewewFb7bZCNRwE7wLCUNzDLaLVA=
|
||||
@ -45,8 +46,12 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
|
18
pkg/template/color.go
Normal file
18
pkg/template/color.go
Normal file
@ -0,0 +1,18 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"github.com/fatih/color"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
var (
|
||||
colorTitle = color.New(color.FgYellow, color.Bold)
|
||||
colorTotalAdd = color.New(color.FgHiGreen, color.Bold)
|
||||
colorTotalRemove = color.New(color.FgRed, color.Bold)
|
||||
|
||||
noColor = tablewriter.Colors{}
|
||||
colorAdd = tablewriter.Colors{tablewriter.FgHiGreenColor, tablewriter.Bold}
|
||||
colorFooter = tablewriter.Color(tablewriter.FgHiBlueColor, tablewriter.Bold)
|
||||
colorHeader = tablewriter.Colors{tablewriter.FgHiCyanColor, tablewriter.Bold}
|
||||
colorRemove = tablewriter.Colors{tablewriter.FgRedColor, tablewriter.Bold}
|
||||
)
|
180
pkg/template/diff_printer.go
Normal file
180
pkg/template/diff_printer.go
Normal file
@ -0,0 +1,180 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
type DiffPrinter struct {
|
||||
w io.Writer
|
||||
writer *tablewriter.Table
|
||||
|
||||
useColor bool
|
||||
title string
|
||||
|
||||
appendCalls int
|
||||
headerLen int
|
||||
}
|
||||
|
||||
func NewDiffPrinter(w io.Writer, hasColor, hasBorder bool) *DiffPrinter {
|
||||
wr := tablewriter.NewWriter(w)
|
||||
wr.SetBorder(hasBorder)
|
||||
wr.SetRowLine(hasBorder)
|
||||
|
||||
return &DiffPrinter{
|
||||
w: w,
|
||||
writer: wr,
|
||||
useColor: hasColor,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DiffPrinter) Render() {
|
||||
if d.appendCalls == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// set the title and the add/remove legend
|
||||
title := strings.ToUpper(d.title)
|
||||
add := "+add"
|
||||
remove := "-remove"
|
||||
if d.useColor {
|
||||
title = colorTitle.Sprint(title)
|
||||
add = colorTotalAdd.Sprint(add)
|
||||
remove = colorTotalRemove.Sprint(remove)
|
||||
}
|
||||
fmt.Fprintf(d.w, "%s %s | %s | unchanged\n", title, add, remove)
|
||||
|
||||
d.setFooter()
|
||||
d.writer.Render()
|
||||
}
|
||||
|
||||
func (d *DiffPrinter) Title(title string) *DiffPrinter {
|
||||
d.title = title
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DiffPrinter) SetHeaders(headers ...string) *DiffPrinter {
|
||||
headers = d.prepend(headers, "+/-")
|
||||
d.headerLen = len(headers)
|
||||
|
||||
d.writer.SetHeader(headers)
|
||||
|
||||
headerColors := make([]tablewriter.Colors, d.headerLen)
|
||||
color := noColor
|
||||
if d.useColor {
|
||||
color = colorHeader
|
||||
}
|
||||
for i := range headerColors {
|
||||
headerColors[i] = color
|
||||
}
|
||||
d.writer.SetHeaderColor(headerColors...)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DiffPrinter) setFooter() *DiffPrinter {
|
||||
footers := make([]string, d.headerLen)
|
||||
if d.headerLen > 1 {
|
||||
footers[len(footers)-2] = "TOTAL"
|
||||
footers[len(footers)-1] = strconv.Itoa(d.appendCalls)
|
||||
} else {
|
||||
footers[0] = "TOTAL: " + strconv.Itoa(d.appendCalls)
|
||||
}
|
||||
|
||||
d.writer.SetFooter(footers)
|
||||
colors := make([]tablewriter.Colors, d.headerLen)
|
||||
color := noColor
|
||||
if d.useColor {
|
||||
color = colorFooter
|
||||
}
|
||||
if d.headerLen > 1 {
|
||||
colors[len(colors)-2] = color
|
||||
colors[len(colors)-1] = color
|
||||
} else {
|
||||
colors[0] = color
|
||||
}
|
||||
d.writer.SetFooterColor(colors...)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DiffPrinter) Append(slc []string) {
|
||||
d.writer.Append(d.prepend(slc, ""))
|
||||
}
|
||||
|
||||
func (d *DiffPrinter) AppendDiff(remove, add []string) {
|
||||
defer func() { d.appendCalls++ }()
|
||||
|
||||
if d.appendCalls > 0 {
|
||||
d.appendBufferLine()
|
||||
}
|
||||
|
||||
lenAdd, lenRemove := len(add), len(remove)
|
||||
preppedAdd, preppedRemove := d.prepend(add, "+"), d.prepend(remove, "-")
|
||||
if lenRemove > 0 && lenAdd == 0 {
|
||||
d.writer.Rich(preppedRemove, d.redRow(len(preppedRemove)))
|
||||
return
|
||||
}
|
||||
if lenAdd > 0 && lenRemove == 0 {
|
||||
d.writer.Rich(preppedAdd, d.greenRow(len(preppedAdd)))
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
addColors = make([]tablewriter.Colors, len(preppedAdd))
|
||||
removeColors = make([]tablewriter.Colors, len(preppedRemove))
|
||||
hasDiff bool
|
||||
)
|
||||
addColor, removeColor := noColor, noColor
|
||||
if d.useColor {
|
||||
addColor, removeColor = colorAdd, colorRemove
|
||||
}
|
||||
for i := 0; i < lenRemove; i++ {
|
||||
if add[i] != remove[i] {
|
||||
hasDiff = true
|
||||
// offset to skip prepended +/- column
|
||||
addColors[i+1], removeColors[i+1] = addColor, removeColor
|
||||
}
|
||||
}
|
||||
|
||||
if !hasDiff {
|
||||
d.writer.Append(d.prepend(add, ""))
|
||||
return
|
||||
}
|
||||
|
||||
addColors[0], removeColors[0] = addColor, removeColor
|
||||
d.writer.Rich(d.prepend(remove, "-"), removeColors)
|
||||
d.writer.Rich(d.prepend(add, "+"), addColors)
|
||||
}
|
||||
|
||||
func (d *DiffPrinter) appendBufferLine() {
|
||||
d.writer.Append([]string{})
|
||||
}
|
||||
|
||||
func (d *DiffPrinter) redRow(i int) []tablewriter.Colors {
|
||||
return d.colorRow(colorRemove, i)
|
||||
}
|
||||
|
||||
func (d *DiffPrinter) greenRow(i int) []tablewriter.Colors {
|
||||
return d.colorRow(colorAdd, i)
|
||||
}
|
||||
|
||||
func (d *DiffPrinter) prepend(slc []string, val string) []string {
|
||||
return append([]string{val}, slc...)
|
||||
}
|
||||
|
||||
func (d *DiffPrinter) colorRow(color tablewriter.Colors, i int) []tablewriter.Colors {
|
||||
colors := make([]tablewriter.Colors, i)
|
||||
for i := range colors {
|
||||
if d.useColor {
|
||||
colors[i] = color
|
||||
} else {
|
||||
colors[i] = noColor
|
||||
}
|
||||
}
|
||||
return colors
|
||||
}
|
67
pkg/template/diff_printer_test.go
Normal file
67
pkg/template/diff_printer_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
package template_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/influx-cli/v2/pkg/template"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDiffPrinter_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
out := bytes.Buffer{}
|
||||
printer := template.NewDiffPrinter(&out, false, true).
|
||||
Title("Example").
|
||||
SetHeaders("Wow", "Such", "A", "Fancy", "Printer")
|
||||
|
||||
printer.Render()
|
||||
require.Empty(t, out.String())
|
||||
}
|
||||
|
||||
func TestDiffPrinter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
out := bytes.Buffer{}
|
||||
printer := template.NewDiffPrinter(&out, false, true).
|
||||
Title("Example").
|
||||
SetHeaders("Wow", "Such", "A", "Fancy", "Printer")
|
||||
|
||||
// Add
|
||||
printer.AppendDiff(nil, []string{"A", "B", "C", "D", "E"})
|
||||
|
||||
// No change
|
||||
printer.Append([]string{"foo", "bar", "baz", "qux", "wat"})
|
||||
|
||||
// Replace
|
||||
printer.AppendDiff(
|
||||
[]string{"1", "200000000000000", "3", "4", "5"},
|
||||
[]string{"9", "8", "7", "6", "5"},
|
||||
)
|
||||
|
||||
// Remove
|
||||
printer.AppendDiff([]string{"x y", "z x", "x y z", "", "y z"}, nil)
|
||||
|
||||
printer.Render()
|
||||
expected := `EXAMPLE +add | -remove | unchanged
|
||||
+-----+-----+-----------------+-------+-------+---------+
|
||||
| +/- | WOW | SUCH | A | FANCY | PRINTER |
|
||||
+-----+-----+-----------------+-------+-------+---------+
|
||||
| + | A | B | C | D | E |
|
||||
+-----+-----+-----------------+-------+-------+---------+
|
||||
| | foo | bar | baz | qux | wat |
|
||||
+-----+-----+-----------------+-------+-------+---------+
|
||||
+-----+-----+-----------------+-------+-------+---------+
|
||||
| - | 1 | 200000000000000 | 3 | 4 | 5 |
|
||||
+-----+-----+-----------------+-------+-------+---------+
|
||||
| + | 9 | 8 | 7 | 6 | 5 |
|
||||
+-----+-----+-----------------+-------+-------+---------+
|
||||
+-----+-----+-----------------+-------+-------+---------+
|
||||
| - | x y | z x | x y z | | y z |
|
||||
+-----+-----+-----------------+-------+-------+---------+
|
||||
| TOTAL | 3 |
|
||||
+-----+-----+-----------------+-------+-------+---------+
|
||||
`
|
||||
require.Equal(t, expected, out.String())
|
||||
}
|
110
pkg/template/table_printer.go
Normal file
110
pkg/template/table_printer.go
Normal file
@ -0,0 +1,110 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
type TablePrinter struct {
|
||||
w io.Writer
|
||||
writer *tablewriter.Table
|
||||
|
||||
useColor bool
|
||||
title string
|
||||
|
||||
headerLen int
|
||||
appendCalls int
|
||||
}
|
||||
|
||||
func NewTablePrinter(w io.Writer, hasColor, hasBorder bool) *TablePrinter {
|
||||
wr := tablewriter.NewWriter(w)
|
||||
wr.SetBorder(hasBorder)
|
||||
wr.SetRowLine(hasBorder)
|
||||
|
||||
return &TablePrinter{
|
||||
w: w,
|
||||
writer: wr,
|
||||
useColor: hasColor,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TablePrinter) Render() {
|
||||
if t.appendCalls == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
title := strings.ToUpper(t.title)
|
||||
if t.useColor {
|
||||
title = colorTitle.Sprint(title)
|
||||
}
|
||||
fmt.Fprintln(t.w, title)
|
||||
|
||||
t.setFooter()
|
||||
t.writer.Render()
|
||||
}
|
||||
|
||||
func (t *TablePrinter) Title(title string) *TablePrinter {
|
||||
t.title = title
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *TablePrinter) SetHeaders(headers ...string) *TablePrinter {
|
||||
t.headerLen = len(headers)
|
||||
t.writer.SetHeader(headers)
|
||||
|
||||
headerColors := make([]tablewriter.Colors, t.headerLen)
|
||||
alignments := make([]int, t.headerLen)
|
||||
|
||||
color := noColor
|
||||
if t.useColor {
|
||||
color = colorHeader
|
||||
}
|
||||
for i, header := range headers {
|
||||
headerColors[i] = color
|
||||
if strings.EqualFold("description", header) {
|
||||
t.writer.SetColMinWidth(i, 30)
|
||||
alignments[i] = tablewriter.ALIGN_LEFT
|
||||
} else {
|
||||
alignments[i] = tablewriter.ALIGN_CENTER
|
||||
}
|
||||
}
|
||||
t.writer.SetHeaderColor(headerColors...)
|
||||
t.writer.SetColumnAlignment(alignments)
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *TablePrinter) setFooter() *TablePrinter {
|
||||
footers := make([]string, t.headerLen)
|
||||
if t.headerLen > 1 {
|
||||
footers[len(footers)-2] = "TOTAL"
|
||||
footers[len(footers)-1] = strconv.Itoa(t.appendCalls)
|
||||
} else {
|
||||
footers[0] = "TOTAL: " + strconv.Itoa(t.appendCalls)
|
||||
}
|
||||
t.writer.SetFooter(footers)
|
||||
|
||||
colors := make([]tablewriter.Colors, t.headerLen)
|
||||
color := noColor
|
||||
if t.useColor {
|
||||
color = colorFooter
|
||||
}
|
||||
if t.headerLen > 1 {
|
||||
colors[len(colors)-2] = color
|
||||
colors[len(colors)-1] = color
|
||||
} else {
|
||||
colors[0] = color
|
||||
}
|
||||
t.writer.SetFooterColor(colors...)
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *TablePrinter) Append(slc []string) {
|
||||
t.appendCalls++
|
||||
t.writer.Append(slc)
|
||||
}
|
70
pkg/template/table_printer_test.go
Normal file
70
pkg/template/table_printer_test.go
Normal file
@ -0,0 +1,70 @@
|
||||
package template_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/influx-cli/v2/pkg/template"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTablePrinter_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
out := bytes.Buffer{}
|
||||
printer := template.NewTablePrinter(&out, false, true).
|
||||
Title("Example").
|
||||
SetHeaders("Wow", "Such", "A", "Fancy", "Printer")
|
||||
|
||||
printer.Render()
|
||||
require.Empty(t, out.String())
|
||||
}
|
||||
|
||||
func TestTablePrinter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
out := bytes.Buffer{}
|
||||
printer := template.NewTablePrinter(&out, false, true).
|
||||
Title("Example").
|
||||
SetHeaders("Wow", "Such", "A", "Fancy", "Printer")
|
||||
|
||||
printer.Append([]string{"foo", "bar", "baz", "qux", "wat"})
|
||||
printer.Append([]string{"veryveryverylongggg", "", "a", "b", "c"})
|
||||
|
||||
printer.Render()
|
||||
expected := `EXAMPLE
|
||||
+---------------------+------+-----+-------+---------+
|
||||
| WOW | SUCH | A | FANCY | PRINTER |
|
||||
+---------------------+------+-----+-------+---------+
|
||||
| foo | bar | baz | qux | wat |
|
||||
+---------------------+------+-----+-------+---------+
|
||||
| veryveryverylongggg | | a | b | c |
|
||||
+---------------------+------+-----+-------+---------+
|
||||
| TOTAL | 2 |
|
||||
+---------------------+------+-----+-------+---------+
|
||||
`
|
||||
require.Equal(t, expected, out.String())
|
||||
}
|
||||
|
||||
func TestTablePrinter_Description(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
out := bytes.Buffer{}
|
||||
printer := template.NewTablePrinter(&out, false, true).
|
||||
Title("Example").
|
||||
SetHeaders("Wow", "Such", "A", "Fancy", "Description")
|
||||
printer.Append([]string{"once", "upon", "a", "time", "short description"})
|
||||
|
||||
printer.Render()
|
||||
// Expect that the description is left-aligned with a min width.
|
||||
expected := `EXAMPLE
|
||||
+------+------+---+-------+--------------------------------+
|
||||
| WOW | SUCH | A | FANCY | DESCRIPTION |
|
||||
+------+------+---+-------+--------------------------------+
|
||||
| once | upon | a | time | short description |
|
||||
+------+------+---+-------+--------------------------------+
|
||||
| TOTAL | 1 |
|
||||
+------+------+---+-------+--------------------------------+
|
||||
`
|
||||
require.Equal(t, expected, out.String())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user