package game import ( "fmt" "strings" ) type tableGlyphs struct { topLeft, topSep, topRight rune side, colSep rune sepLeft, sepCross, sepRight rune botLeft, botSep, botRight rune fillH, fillHSep rune } func tableGlyphSet(unicode bool) tableGlyphs { if unicode { return tableGlyphs{ topLeft: '╔', topSep: '╤', topRight: '╗', side: '║', colSep: '│', sepLeft: '╟', sepCross: '┼', sepRight: '╢', botLeft: '╚', botSep: '╧', botRight: '╝', fillH: '═', fillHSep: '─', } } return tableGlyphs{ topLeft: '+', topSep: '+', topRight: '+', side: '|', colSep: '|', sepLeft: '+', sepCross: '+', sepRight: '+', botLeft: '+', botSep: '+', botRight: '+', fillH: '-', fillHSep: '-', } } type Table struct { Columns []string Rows [][]string } func (t *Table) Render(unicode bool) []string { g := tableGlyphSet(unicode) nCols := len(t.Columns) if nCols == 0 { return nil } colWidths := make([]int, nCols) for i, h := range t.Columns { colWidths[i] = len(h) } for _, row := range t.Rows { for i, cell := range row { if i >= nCols { break } if len(cell) > colWidths[i] { colWidths[i] = len(cell) } } } makeSep := func(left, cross, right rune, fill rune) string { var b strings.Builder b.WriteRune(left) for i := 0; i < nCols; i++ { if i > 0 { b.WriteRune(cross) } b.WriteString(strings.Repeat(string(fill), colWidths[i]+2)) } b.WriteRune(right) return b.String() } makeRow := func(cells []string) string { var b strings.Builder b.WriteRune(g.side) for i := 0; i < nCols; i++ { if i > 0 { b.WriteRune(g.colSep) } cell := "" if i < len(cells) { cell = cells[i] } b.WriteString(fmt.Sprintf(" %-*s ", colWidths[i], cell)) } b.WriteRune(g.side) return b.String() } var out []string out = append(out, makeSep(g.topLeft, g.topSep, g.topRight, g.fillH)) out = append(out, makeRow(t.Columns)) out = append(out, makeSep(g.sepLeft, g.sepCross, g.sepRight, g.fillHSep)) for _, row := range t.Rows { out = append(out, makeRow(row)) } out = append(out, makeSep(g.botLeft, g.botSep, g.botRight, g.fillH)) return out }