initial commit
This commit is contained in:
commit
8e9954514b
7
col_spec.go
Normal file
7
col_spec.go
Normal file
@ -0,0 +1,7 @@
|
||||
package tableprint
|
||||
|
||||
type ColSpec struct {
|
||||
Name string
|
||||
MaxWidth int
|
||||
MaxHeight int
|
||||
}
|
16
go.mod
Normal file
16
go.mod
Normal file
@ -0,0 +1,16 @@
|
||||
module git.milar.in/milarin/tprint
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
git.milar.in/milarin/gmath v0.0.2
|
||||
git.milar.in/milarin/slices v0.0.3
|
||||
github.com/fatih/color v1.13.0
|
||||
)
|
||||
|
||||
require (
|
||||
git.milar.in/milarin/channel v0.0.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
||||
)
|
17
go.sum
Normal file
17
go.sum
Normal file
@ -0,0 +1,17 @@
|
||||
git.milar.in/milarin/channel v0.0.9 h1:vYJnXOaIn/+mng1+8CyepQgDfRse2s4FSLNc7zv85To=
|
||||
git.milar.in/milarin/channel v0.0.9/go.mod h1:We83LTI8S7u7II3pD+A2ChCDWJfCkcBUCUqii9HjTtM=
|
||||
git.milar.in/milarin/gmath v0.0.2 h1:avz+75f8XqAYA1wEB6kis0R5xvRuepBKTqBuJBjh6Yw=
|
||||
git.milar.in/milarin/gmath v0.0.2/go.mod h1:HDLftG5RLpiNGKiIWh+O2G1PYkNzyLDADO8Cd/1abiE=
|
||||
git.milar.in/milarin/slices v0.0.3 h1:kzaLrE/G4rO2DQq3nVk2TYbuqOsiauLHClVUpgSZM8s=
|
||||
git.milar.in/milarin/slices v0.0.3/go.mod h1:XRNfE99aNKeaPOY1phjOlpIQqeGCW1LOqqh8UHS+vAk=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
141
table.go
Normal file
141
table.go
Normal file
@ -0,0 +1,141 @@
|
||||
package tableprint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.milar.in/milarin/gmath"
|
||||
"git.milar.in/milarin/slices"
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
head [][]string
|
||||
|
||||
rowSep bool
|
||||
headheight int
|
||||
maxcols int
|
||||
|
||||
colwidth []int
|
||||
rowheight []int
|
||||
|
||||
data [][][]string
|
||||
}
|
||||
|
||||
func NewDynamicTable(head ...string) *Table {
|
||||
thead := slices.Map(head, func(s string) []string { return strings.Split(s, "\n") })
|
||||
return &Table{
|
||||
head: thead,
|
||||
colwidth: slices.Map(head, func(s string) int { return strLen(s) }),
|
||||
headheight: len(slices.Search(thead, maxLengthSlice[string])),
|
||||
rowheight: []int{},
|
||||
maxcols: len(thead),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Table) AddRow(row ...interface{}) {
|
||||
irow := slices.Map(row, func(v interface{}) []string { return strings.Split(fmt.Sprint(v), "\n") })
|
||||
t.data = append(t.data, irow)
|
||||
|
||||
rowheight := len(slices.Search(irow, maxLengthSlice[string]))
|
||||
t.rowheight = append(t.rowheight, rowheight)
|
||||
if rowheight > 1 {
|
||||
t.rowSep = true
|
||||
}
|
||||
|
||||
t.maxcols = gmath.Max(t.maxcols, len(irow))
|
||||
|
||||
for i, cell := range irow {
|
||||
maxLineLength := strLen(slices.Search(cell, maxLengthStr))
|
||||
if i < len(t.colwidth) {
|
||||
t.colwidth[i] = gmath.Max(t.colwidth[i], maxLineLength)
|
||||
} else {
|
||||
t.colwidth = append(t.colwidth, maxLineLength)
|
||||
}
|
||||
|
||||
if i >= len(t.head) {
|
||||
headname := "unknown column"
|
||||
t.head = append(t.head, []string{headname})
|
||||
t.colwidth[i] = gmath.Max(t.colwidth[i], strLen(headname))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Table) String() string {
|
||||
b := new(strings.Builder)
|
||||
|
||||
b.WriteRune('┌')
|
||||
for i, colwidth := range t.colwidth {
|
||||
b.WriteString(strings.Repeat("─", colwidth))
|
||||
if i < len(t.colwidth)-1 {
|
||||
b.WriteRune('┬')
|
||||
}
|
||||
}
|
||||
b.WriteRune('┐')
|
||||
b.WriteRune('\n')
|
||||
|
||||
bold := color.New(color.Bold)
|
||||
t.printRow(b, bold.Sprint, t.head, t.headheight)
|
||||
|
||||
if len(t.data) > 0 {
|
||||
t.addNextCellLine(b)
|
||||
} else {
|
||||
t.addLastCellLine(b)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
for i, row := range t.data {
|
||||
t.printRow(b, fmt.Sprint, row, t.rowheight[i])
|
||||
if t.rowSep && i < len(t.data)-1 {
|
||||
t.addNextCellLine(b)
|
||||
}
|
||||
}
|
||||
|
||||
t.addLastCellLine(b)
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func (t *Table) printRow(b *strings.Builder, printFunc func(...interface{}) string, rowData [][]string, rowHeight int) {
|
||||
for lineIndex := 0; lineIndex < rowHeight; lineIndex++ {
|
||||
b.WriteRune('│')
|
||||
for colIndex := 0; colIndex < t.maxcols; colIndex++ {
|
||||
line := ""
|
||||
|
||||
if colIndex < len(rowData) {
|
||||
cell := rowData[colIndex]
|
||||
if lineIndex < len(cell) {
|
||||
line = cell[lineIndex]
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteString(printFunc(padStringRight(line, ' ', t.colwidth[colIndex])))
|
||||
b.WriteRune('│')
|
||||
}
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Table) addNextCellLine(b *strings.Builder) {
|
||||
b.WriteRune('├')
|
||||
for i, colwidth := range t.colwidth {
|
||||
b.WriteString(strings.Repeat("─", colwidth))
|
||||
if i < len(t.colwidth)-1 {
|
||||
b.WriteRune('┼')
|
||||
}
|
||||
}
|
||||
b.WriteRune('┤')
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
|
||||
func (t *Table) addLastCellLine(b *strings.Builder) {
|
||||
b.WriteRune('└')
|
||||
for i, colwidth := range t.colwidth {
|
||||
b.WriteString(strings.Repeat("─", colwidth))
|
||||
if i < len(t.colwidth)-1 {
|
||||
b.WriteRune('┴')
|
||||
}
|
||||
}
|
||||
b.WriteRune('┘')
|
||||
b.WriteRune('\n')
|
||||
}
|
48
utils.go
Normal file
48
utils.go
Normal file
@ -0,0 +1,48 @@
|
||||
package tableprint
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func maxLengthStr(a, b string) string {
|
||||
if strLen(a) > strLen(b) {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func maxLengthSlice[T any](a, b []T) []T {
|
||||
if len(a) > len(b) {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func strLen(str string) int {
|
||||
l := 0
|
||||
|
||||
runes := []rune(str)
|
||||
colored := false
|
||||
|
||||
for i := 0; i < len(runes); i++ {
|
||||
if unicode.IsControl(runes[i]) {
|
||||
if colored {
|
||||
i += 3
|
||||
} else {
|
||||
i += 4
|
||||
}
|
||||
colored = !colored
|
||||
continue
|
||||
}
|
||||
|
||||
l++
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func padStringRight(str string, pad rune, length int) string {
|
||||
padding := length - strLen(str)
|
||||
return str + strings.Repeat(string(pad), padding)
|
||||
}
|
Loading…
Reference in New Issue
Block a user