initial commit

This commit is contained in:
Timon Ringwald 2022-08-25 16:00:13 +02:00
commit 8e9954514b
5 changed files with 229 additions and 0 deletions

7
col_spec.go Normal file
View File

@ -0,0 +1,7 @@
package tableprint
type ColSpec struct {
Name string
MaxWidth int
MaxHeight int
}

16
go.mod Normal file
View 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
View 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
View 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
View 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)
}