zutils/pkg/formatter/table.go
selamanapps aeae34365c feat: add zutils v0.2.0 with professional table formatting
- Add directory information with Unicode table borders
- Add network information with professional formatting
- Add version command
- Add comprehensive documentation (README.md, DEVELOPMENT.md)
- Improve table output with proper borders and alignment
- Add project structure (cmd/, pkg/ directories)
2026-05-02 00:05:44 +03:00

220 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())
}