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') }