221 lines
4.8 KiB
Go
221 lines
4.8 KiB
Go
|
|
package formatter
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"github.com/zemenawi/zutils/pkg/colors"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Table represents a formatted table with better formatting
|
||
|
|
type Table struct {
|
||
|
|
headers []string
|
||
|
|
rows [][]string
|
||
|
|
columnWidths []int
|
||
|
|
showHeader bool
|
||
|
|
showBorder bool
|
||
|
|
padding int
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewTable creates a new table
|
||
|
|
func NewTable(headers []string) *Table {
|
||
|
|
return &Table{
|
||
|
|
headers: headers,
|
||
|
|
rows: make([][]string, 0),
|
||
|
|
columnWidths: calculateColumnWidths(headers, make([][]string, 0)),
|
||
|
|
showHeader: true,
|
||
|
|
showBorder: true,
|
||
|
|
padding: 2,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// SetShowBorder sets whether to show borders
|
||
|
|
func (t *Table) SetShowBorder(show bool) *Table {
|
||
|
|
t.showBorder = show
|
||
|
|
return t
|
||
|
|
}
|
||
|
|
|
||
|
|
// SetShowHeader sets whether to show header
|
||
|
|
func (t *Table) SetShowHeader(show bool) *Table {
|
||
|
|
t.showHeader = show
|
||
|
|
return t
|
||
|
|
}
|
||
|
|
|
||
|
|
// SetPadding sets the padding
|
||
|
|
func (t *Table) SetPadding(padding int) *Table {
|
||
|
|
t.padding = padding
|
||
|
|
return t
|
||
|
|
}
|
||
|
|
|
||
|
|
// AddRow adds a row to the table
|
||
|
|
func (t *Table) AddRow(row []string) *Table {
|
||
|
|
if len(row) != len(t.headers) {
|
||
|
|
return t
|
||
|
|
}
|
||
|
|
t.rows = append(t.rows, row)
|
||
|
|
t.columnWidths = calculateColumnWidths(t.headers, t.rows)
|
||
|
|
return t
|
||
|
|
}
|
||
|
|
|
||
|
|
// Render renders the table as a string
|
||
|
|
func (t *Table) Render() string {
|
||
|
|
var builder strings.Builder
|
||
|
|
|
||
|
|
if t.showBorder {
|
||
|
|
builder.WriteString(t.renderTopBorder())
|
||
|
|
}
|
||
|
|
|
||
|
|
if t.showHeader {
|
||
|
|
builder.WriteString(t.renderRow(t.headers, true))
|
||
|
|
if t.showBorder {
|
||
|
|
builder.WriteString(t.renderSeparator())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, row := range t.rows {
|
||
|
|
builder.WriteString(t.renderRow(row, false))
|
||
|
|
}
|
||
|
|
|
||
|
|
if t.showBorder {
|
||
|
|
builder.WriteString(t.renderBottomBorder())
|
||
|
|
}
|
||
|
|
|
||
|
|
return builder.String()
|
||
|
|
}
|
||
|
|
|
||
|
|
// PrintTable prints a table to stdout
|
||
|
|
func PrintTable(headers []string, rows [][]string, borderColor string) {
|
||
|
|
table := NewTable(headers)
|
||
|
|
for _, row := range rows {
|
||
|
|
table.AddRow(row)
|
||
|
|
}
|
||
|
|
|
||
|
|
output := table.Render()
|
||
|
|
fmt.Print(borderColor)
|
||
|
|
fmt.Print(output)
|
||
|
|
fmt.Println(colors.Reset)
|
||
|
|
}
|
||
|
|
|
||
|
|
// PrintColoredTable prints a table with colored headers
|
||
|
|
func PrintColoredTable(headers []string, rows [][]string, headerColor string) {
|
||
|
|
// Add colors to headers
|
||
|
|
coloredHeaders := make([]string, len(headers))
|
||
|
|
for i, header := range headers {
|
||
|
|
coloredHeaders[i] = headerColor + header + colors.Reset
|
||
|
|
}
|
||
|
|
|
||
|
|
table := NewTable(coloredHeaders)
|
||
|
|
for _, row := range rows {
|
||
|
|
table.AddRow(row)
|
||
|
|
}
|
||
|
|
|
||
|
|
table.showBorder = false
|
||
|
|
fmt.Print(table.Render())
|
||
|
|
}
|
||
|
|
|
||
|
|
// renderRow renders a single row
|
||
|
|
func (t *Table) renderRow(row []string, isHeader bool) string {
|
||
|
|
var builder strings.Builder
|
||
|
|
builder.WriteString("│")
|
||
|
|
|
||
|
|
for i, cell := range row {
|
||
|
|
if i > 0 {
|
||
|
|
builder.WriteString("│")
|
||
|
|
}
|
||
|
|
|
||
|
|
builder.WriteString(t.padCell(cell, t.columnWidths[i], isHeader))
|
||
|
|
}
|
||
|
|
|
||
|
|
builder.WriteString("│\n")
|
||
|
|
return builder.String()
|
||
|
|
}
|
||
|
|
|
||
|
|
// padCell pads a cell to the correct width
|
||
|
|
func (t *Table) padCell(cell string, width int, isHeader bool) string {
|
||
|
|
padChar := " "
|
||
|
|
|
||
|
|
// Left padding
|
||
|
|
builder := strings.Builder{}
|
||
|
|
for i := 0; i < t.padding; i++ {
|
||
|
|
builder.WriteString(padChar)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Cell content
|
||
|
|
builder.WriteString(cell)
|
||
|
|
|
||
|
|
// Right padding - pad to exact column width
|
||
|
|
needed := width - len(cell)
|
||
|
|
for i := 0; i < needed+t.padding; i++ {
|
||
|
|
builder.WriteString(padChar)
|
||
|
|
}
|
||
|
|
|
||
|
|
return builder.String()
|
||
|
|
}
|
||
|
|
|
||
|
|
// renderTopBorder renders the top border
|
||
|
|
func (t *Table) renderTopBorder() string {
|
||
|
|
return t.renderBorderLine("╔", "═", "╗", "╦")
|
||
|
|
}
|
||
|
|
|
||
|
|
// renderBottomBorder renders the bottom border
|
||
|
|
func (t *Table) renderBottomBorder() string {
|
||
|
|
return t.renderBorderLine("╚", "═", "╝", "╩")
|
||
|
|
}
|
||
|
|
|
||
|
|
// renderSeparator renders the separator between header and rows
|
||
|
|
func (t *Table) renderSeparator() string {
|
||
|
|
return t.renderBorderLine("╠", "─", "╣", "╪")
|
||
|
|
}
|
||
|
|
|
||
|
|
// renderBorderLine renders a border line
|
||
|
|
func (t *Table) renderBorderLine(left, middle, right, junction string) string {
|
||
|
|
builder := strings.Builder{}
|
||
|
|
builder.WriteString(left)
|
||
|
|
|
||
|
|
for i, width := range t.columnWidths {
|
||
|
|
if i > 0 {
|
||
|
|
builder.WriteString(junction)
|
||
|
|
}
|
||
|
|
for j := 0; j < width+2*t.padding; j++ {
|
||
|
|
builder.WriteString(middle)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
builder.WriteString(right)
|
||
|
|
builder.WriteString("\n")
|
||
|
|
return builder.String()
|
||
|
|
}
|
||
|
|
|
||
|
|
// calculateColumnWidths calculates the width of each column
|
||
|
|
func calculateColumnWidths(headers []string, rows [][]string) []int {
|
||
|
|
widths := make([]int, len(headers))
|
||
|
|
|
||
|
|
// Start with header widths
|
||
|
|
for i, header := range headers {
|
||
|
|
widths[i] = len(header)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Adjust based on row content
|
||
|
|
for _, row := range rows {
|
||
|
|
for i, cell := range row {
|
||
|
|
if i < len(widths) && len(cell) > widths[i] {
|
||
|
|
widths[i] = len(cell)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return widths
|
||
|
|
}
|
||
|
|
|
||
|
|
// PrintCompactTable prints a compact table without borders
|
||
|
|
func PrintCompactTable(headers []string, rows [][]string) {
|
||
|
|
table := NewTable(headers)
|
||
|
|
for _, row := range rows {
|
||
|
|
table.AddRow(row)
|
||
|
|
}
|
||
|
|
table.showBorder = false
|
||
|
|
table.showHeader = false
|
||
|
|
table.padding = 2
|
||
|
|
fmt.Print(table.Render())
|
||
|
|
}
|