package tprint import ( "fmt" "strings" "git.milar.in/milarin/gmath" "git.milar.in/milarin/slices" ) type Table struct { head [][]string rowSep bool headheight int maxcols int colwidth []int rowheight []int data [][][]string } func NewTable(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) StringNoHead() 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') for i, row := range t.data { t.printRow(b, row, t.rowheight[i]) if t.rowSep && i < len(t.data)-1 { t.addNextCellLine(b) } } t.addLastCellLine(b) return b.String() } 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') t.printRow(b, t.head, t.headheight) 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') for i, row := range t.data { t.printRow(b, 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, 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(fmt.Sprint(padStringRight(line, ' ', t.colwidth[colIndex]))) if colIndex < t.maxcols-1 { b.WriteRune('│') } } b.WriteString("┃\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.WriteString("┛\n") }