mirror of
https://github.com/miguelmota/cointop
synced 2024-11-16 21:25:38 +00:00
e1aded93e8
* Redundant type conversions * Remove redudant type declarations. Inline one-line constructors. Remove unnecessary brackets. * Simplify name - it's used via package name anyway * Simplify variable initializations * Change `var x uint = Y` to `x := uint(Y)` * More shorthand initialization
280 lines
4.9 KiB
Go
280 lines
4.9 KiB
Go
package table
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"sort"
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
"github.com/acarl005/stripansi"
|
|
"github.com/cointop-sh/cointop/pkg/pad"
|
|
"github.com/cointop-sh/cointop/pkg/table/align"
|
|
)
|
|
|
|
// Table table
|
|
type Table struct {
|
|
cols Cols
|
|
rows Rows
|
|
sort []SortBy
|
|
width int
|
|
HideColumHeaders bool
|
|
}
|
|
|
|
// NewTable new table
|
|
func NewTable() *Table {
|
|
return &Table{}
|
|
}
|
|
|
|
// SetWidth set table width
|
|
func (t *Table) SetWidth(w int) *Table {
|
|
t.width = w
|
|
return t
|
|
}
|
|
|
|
// AddCol add column
|
|
func (t *Table) AddCol(n string) *Col {
|
|
c := &Col{name: n}
|
|
t.cols = append(t.cols, c)
|
|
return c
|
|
}
|
|
|
|
// AddRow add row
|
|
func (t *Table) AddRow(v ...interface{}) *Row {
|
|
r := &Row{table: t, values: v, strValues: make([]string, len(v))}
|
|
t.rows = append(t.rows, r)
|
|
return r
|
|
}
|
|
|
|
// AddRowCells add row using cells
|
|
func (t *Table) AddRowCells(cells ...*RowCell) *Row {
|
|
t.SetNumCol(len(cells))
|
|
v := make([]interface{}, len(cells))
|
|
for i, item := range cells {
|
|
v[i] = item.String()
|
|
}
|
|
return t.AddRow(v...)
|
|
}
|
|
|
|
// SetNumCol sets the number of columns
|
|
func (t *Table) SetNumCol(count int) {
|
|
for i := 0; i < count; i++ {
|
|
t.AddCol("")
|
|
}
|
|
}
|
|
|
|
// SortAscFn sort ascending function
|
|
func (t *Table) SortAscFn(n string, fn SortFn) *Table {
|
|
i := t.cols.Index(n)
|
|
s := SortBy{index: i, order: SortAsc, sortFn: fn}
|
|
t.sort = append(t.sort, s)
|
|
return t
|
|
}
|
|
|
|
// SortAsc sort ascending
|
|
func (t *Table) SortAsc(n string) *Table {
|
|
return t.SortAscFn(n, nil)
|
|
}
|
|
|
|
// SortDescFn sort descending function
|
|
func (t *Table) SortDescFn(n string, fn SortFn) *Table {
|
|
i := t.cols.Index(n)
|
|
s := SortBy{index: i, order: SortDesc, sortFn: fn}
|
|
t.sort = append(t.sort, s)
|
|
return t
|
|
}
|
|
|
|
// SortDesc sort descending
|
|
func (t *Table) SortDesc(n string) *Table {
|
|
return t.SortDescFn(n, nil)
|
|
}
|
|
|
|
// Sort sort
|
|
func (t *Table) Sort() *Table {
|
|
if len(t.sort) > 0 {
|
|
sort.Sort(t.rows)
|
|
}
|
|
return t
|
|
}
|
|
|
|
func (t *Table) colWidth() int {
|
|
width := 0
|
|
for _, c := range t.cols {
|
|
if c.hide {
|
|
continue
|
|
}
|
|
|
|
width += c.width
|
|
}
|
|
return width
|
|
}
|
|
|
|
func (t *Table) normalizeColWidthPerc() {
|
|
perc := 0
|
|
for _, c := range t.cols {
|
|
if c.hide {
|
|
continue
|
|
}
|
|
|
|
perc += c.minWidthPerc
|
|
}
|
|
|
|
for _, c := range t.cols {
|
|
if c.hide {
|
|
continue
|
|
}
|
|
|
|
c.perc = float32(c.minWidthPerc) / float32(perc)
|
|
}
|
|
}
|
|
|
|
// Format format table
|
|
func (t *Table) Format() *Table {
|
|
for _, c := range t.cols {
|
|
s := stripansi.Strip(c.name)
|
|
c.width = utf8.RuneCountInString(s) + 1
|
|
if c.minWidth > c.width {
|
|
c.width = c.minWidth
|
|
}
|
|
}
|
|
|
|
for _, r := range t.rows {
|
|
for j, v := range r.values {
|
|
c := t.cols[j]
|
|
|
|
if c.hide {
|
|
continue
|
|
}
|
|
|
|
if c.formatFn != nil {
|
|
r.strValues[j] = fmt.Sprintf("%s", c.formatFn(v))
|
|
} else if c.format != "" {
|
|
r.strValues[j] = fmt.Sprintf(c.format, v)
|
|
} else {
|
|
r.strValues[j] = fmt.Sprintf("%v", v)
|
|
}
|
|
|
|
s := stripansi.Strip(r.strValues[j])
|
|
runeCount := utf8.RuneCountInString(s)
|
|
if runeCount > t.cols[j].width {
|
|
t.cols[j].width = runeCount
|
|
}
|
|
}
|
|
}
|
|
|
|
//t.normalizeColWidthPerc()
|
|
unused := t.width - t.colWidth()
|
|
if unused <= 0 {
|
|
return t
|
|
}
|
|
|
|
for _, c := range t.cols {
|
|
if c.hide {
|
|
continue
|
|
}
|
|
|
|
if c.perc > 0 {
|
|
c.width += int(float32(unused) * c.perc)
|
|
}
|
|
}
|
|
|
|
var i int
|
|
for i = len(t.cols) - 1; i >= 0; i-- {
|
|
if t.cols[i].hide {
|
|
continue
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
if len(t.cols) > 0 {
|
|
t.cols[i].width += t.width - t.colWidth()
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
// Fprint write
|
|
func (t *Table) Fprint(w io.Writer) {
|
|
if !t.HideColumHeaders {
|
|
for _, c := range t.cols {
|
|
if c.hide {
|
|
continue
|
|
}
|
|
|
|
var s string
|
|
switch c.align {
|
|
case AlignLeft:
|
|
s = align.Left(c.name+" ", c.width)
|
|
case AlignRight:
|
|
s = align.Right(c.name+" ", c.width)
|
|
case AlignCenter:
|
|
s = align.Center(c.name+" ", c.width)
|
|
}
|
|
|
|
fmt.Fprintf(w, "%s", s)
|
|
}
|
|
fmt.Fprintf(w, "\n")
|
|
|
|
for _, c := range t.cols {
|
|
if c.hide {
|
|
continue
|
|
}
|
|
|
|
fmt.Fprintf(w, strings.Repeat("─", c.width))
|
|
}
|
|
fmt.Fprintf(w, "\n")
|
|
}
|
|
|
|
for _, r := range t.rows {
|
|
for i, v := range r.strValues {
|
|
c := t.cols[i]
|
|
|
|
if c.hide {
|
|
continue
|
|
}
|
|
|
|
var s string
|
|
switch c.align {
|
|
case AlignLeft:
|
|
s = align.Left(v, c.width)
|
|
case AlignRight:
|
|
s = align.Right(v, c.width)
|
|
case AlignCenter:
|
|
s = align.Center(v, c.width)
|
|
}
|
|
|
|
fmt.Fprintf(w, "%s", s)
|
|
}
|
|
// fill in rest of row with empty spaces to highlight all of row
|
|
fmt.Fprintf(w, strings.Repeat(" ", t.width)+"\n")
|
|
}
|
|
}
|
|
|
|
// RowCount returns the number of rows
|
|
func (t *Table) RowCount() int {
|
|
return len(t.rows)
|
|
}
|
|
|
|
// RowCell is a row cell struct
|
|
type RowCell struct {
|
|
LeftMargin int
|
|
RightMargin int
|
|
Width int
|
|
LeftAlign bool
|
|
Color func(a ...interface{}) string
|
|
Text string
|
|
}
|
|
|
|
// String returns row cell as string
|
|
func (rc *RowCell) String() string {
|
|
t := rc.Text
|
|
if rc.LeftAlign {
|
|
t = pad.Right(t, rc.Width, " ")
|
|
} else {
|
|
t = fmt.Sprintf("%"+fmt.Sprintf("%v", rc.Width)+"s", t)
|
|
}
|
|
t = strings.Repeat(" ", rc.LeftMargin) + t + strings.Repeat(" ", rc.RightMargin)
|
|
return rc.Color(t)
|
|
}
|