Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
package tw
// CellFormatting holds formatting options for table cells.
type CellFormatting struct {
AutoWrap int // Wrapping behavior (e.g., WrapTruncate, WrapNormal)
AutoFormat State // Enables automatic formatting (e.g., title case for headers)
// Deprecated: Kept for backward compatibility. Use CellConfig.CellMerging.Mode instead.
// This will be removed in a future version.
MergeMode int
// Deprecated: Kept for backward compatibility. Use CellConfig.Alignment instead.
// This will be removed in a future version.
Alignment Align
}
// CellMerging holds the configuration for how cells should be merged.
// This new struct replaces the deprecated MergeMode.
type CellMerging struct {
// Mode is a bitmask specifying the type of merge (e.g., MergeHorizontal, MergeVertical).
Mode int
// ByColumnIndex specifies which column indices should be considered for merging.
// If the mapper is nil or empty, merging applies to all columns (if Mode is set).
// Otherwise, only columns with an index present as a key will be merged.
ByColumnIndex Mapper[int, bool]
// ByRowIndex is reserved for future features to specify merging on specific rows.
ByRowIndex Mapper[int, bool]
}
// CellPadding defines padding settings for table cells.
type CellPadding struct {
Global Padding // Default padding applied to all cells
PerColumn []Padding // Column-specific padding overrides
}
// CellFilter defines filtering functions for cell content.
type CellFilter struct {
Global func([]string) []string // Processes the entire row
PerColumn []func(string) string // Processes individual cells by column
}
// CellCallbacks holds callback functions for cell processing.
// Note: These are currently placeholders and not fully implemented.
type CellCallbacks struct {
Global func() // Global callback applied to all cells
PerColumn []func() // Column-specific callbacks
}
// CellAlignment defines alignment settings for table cells.
type CellAlignment struct {
Global Align // Default alignment applied to all cells
PerColumn []Align // Column-specific alignment overrides
}
// CellConfig combines formatting, padding, and callback settings for a table section.
type CellConfig struct {
Formatting CellFormatting // Cell formatting options
Padding CellPadding // Padding configuration
Callbacks CellCallbacks // Callback functions (unused)
Filter CellFilter // Function to filter cell content (renamed from Filter Filter)
Alignment CellAlignment // Alignment configuration for cells
ColMaxWidths CellWidth // Per-column maximum width overrides
Merging CellMerging // Merging holds all configuration related to cell merging.
// Deprecated: use Alignment.PerColumn instead. Will be removed in a future version.
// will be removed soon
ColumnAligns []Align // Per-column alignment overrides
}
type CellWidth struct {
Global int
PerColumn Mapper[int, int]
}
func (c CellWidth) Constrained() bool {
return c.Global > 0 || c.PerColumn.Len() > 0
}
+137
View File
@@ -0,0 +1,137 @@
package tw
// Deprecated: SymbolASCII is deprecated; use Glyphs with StyleASCII instead.
// this will be removed soon
type SymbolASCII struct{}
// SymbolASCII symbol methods
func (s *SymbolASCII) Name() string { return StyleNameASCII.String() }
func (s *SymbolASCII) Center() string { return "+" }
func (s *SymbolASCII) Row() string { return "-" }
func (s *SymbolASCII) Column() string { return "|" }
func (s *SymbolASCII) TopLeft() string { return "+" }
func (s *SymbolASCII) TopMid() string { return "+" }
func (s *SymbolASCII) TopRight() string { return "+" }
func (s *SymbolASCII) MidLeft() string { return "+" }
func (s *SymbolASCII) MidRight() string { return "+" }
func (s *SymbolASCII) BottomLeft() string { return "+" }
func (s *SymbolASCII) BottomMid() string { return "+" }
func (s *SymbolASCII) BottomRight() string { return "+" }
func (s *SymbolASCII) HeaderLeft() string { return "+" }
func (s *SymbolASCII) HeaderMid() string { return "+" }
func (s *SymbolASCII) HeaderRight() string { return "+" }
// Deprecated: SymbolUnicode is deprecated; use Glyphs with appropriate styles (e.g., StyleLight, StyleHeavy) instead.
// this will be removed soon
type SymbolUnicode struct {
row string
column string
center string
corners [9]string // [topLeft, topMid, topRight, midLeft, center, midRight, bottomLeft, bottomMid, bottomRight]
}
// SymbolUnicode symbol methods
func (s *SymbolUnicode) Name() string { return "unicode" }
func (s *SymbolUnicode) Center() string { return s.center }
func (s *SymbolUnicode) Row() string { return s.row }
func (s *SymbolUnicode) Column() string { return s.column }
func (s *SymbolUnicode) TopLeft() string { return s.corners[0] }
func (s *SymbolUnicode) TopMid() string { return s.corners[1] }
func (s *SymbolUnicode) TopRight() string { return s.corners[2] }
func (s *SymbolUnicode) MidLeft() string { return s.corners[3] }
func (s *SymbolUnicode) MidRight() string { return s.corners[5] }
func (s *SymbolUnicode) BottomLeft() string { return s.corners[6] }
func (s *SymbolUnicode) BottomMid() string { return s.corners[7] }
func (s *SymbolUnicode) BottomRight() string { return s.corners[8] }
func (s *SymbolUnicode) HeaderLeft() string { return s.MidLeft() }
func (s *SymbolUnicode) HeaderMid() string { return s.Center() }
func (s *SymbolUnicode) HeaderRight() string { return s.MidRight() }
// Deprecated: SymbolMarkdown is deprecated; use Glyphs with StyleMarkdown instead.
// this will be removed soon
type SymbolMarkdown struct{}
// SymbolMarkdown symbol methods
func (s *SymbolMarkdown) Name() string { return StyleNameMarkdown.String() }
func (s *SymbolMarkdown) Center() string { return "|" }
func (s *SymbolMarkdown) Row() string { return "-" }
func (s *SymbolMarkdown) Column() string { return "|" }
func (s *SymbolMarkdown) TopLeft() string { return "" }
func (s *SymbolMarkdown) TopMid() string { return "" }
func (s *SymbolMarkdown) TopRight() string { return "" }
func (s *SymbolMarkdown) MidLeft() string { return "|" }
func (s *SymbolMarkdown) MidRight() string { return "|" }
func (s *SymbolMarkdown) BottomLeft() string { return "" }
func (s *SymbolMarkdown) BottomMid() string { return "" }
func (s *SymbolMarkdown) BottomRight() string { return "" }
func (s *SymbolMarkdown) HeaderLeft() string { return "|" }
func (s *SymbolMarkdown) HeaderMid() string { return "|" }
func (s *SymbolMarkdown) HeaderRight() string { return "|" }
// Deprecated: SymbolNothing is deprecated; use Glyphs with StyleNone instead.
// this will be removed soon
type SymbolNothing struct{}
// SymbolNothing symbol methods
func (s *SymbolNothing) Name() string { return StyleNameNothing.String() }
func (s *SymbolNothing) Center() string { return "" }
func (s *SymbolNothing) Row() string { return "" }
func (s *SymbolNothing) Column() string { return "" }
func (s *SymbolNothing) TopLeft() string { return "" }
func (s *SymbolNothing) TopMid() string { return "" }
func (s *SymbolNothing) TopRight() string { return "" }
func (s *SymbolNothing) MidLeft() string { return "" }
func (s *SymbolNothing) MidRight() string { return "" }
func (s *SymbolNothing) BottomLeft() string { return "" }
func (s *SymbolNothing) BottomMid() string { return "" }
func (s *SymbolNothing) BottomRight() string { return "" }
func (s *SymbolNothing) HeaderLeft() string { return "" }
func (s *SymbolNothing) HeaderMid() string { return "" }
func (s *SymbolNothing) HeaderRight() string { return "" }
// Deprecated: SymbolGraphical is deprecated; use Glyphs with StyleGraphical instead.
// this will be removed soon
type SymbolGraphical struct{}
// SymbolGraphical symbol methods
func (s *SymbolGraphical) Name() string { return StyleNameGraphical.String() }
func (s *SymbolGraphical) Center() string { return "🟧" } // Orange square (matches mid junctions)
func (s *SymbolGraphical) Row() string { return "🟥" } // Red square (matches corners)
func (s *SymbolGraphical) Column() string { return "🟦" } // Blue square (vertical line)
func (s *SymbolGraphical) TopLeft() string { return "🟥" } // Top-left corner
func (s *SymbolGraphical) TopMid() string { return "🔳" } // Top junction
func (s *SymbolGraphical) TopRight() string { return "🟥" } // Top-right corner
func (s *SymbolGraphical) MidLeft() string { return "🟧" } // Left junction
func (s *SymbolGraphical) MidRight() string { return "🟧" } // Right junction
func (s *SymbolGraphical) BottomLeft() string { return "🟥" } // Bottom-left corner
func (s *SymbolGraphical) BottomMid() string { return "🔳" } // Bottom junction
func (s *SymbolGraphical) BottomRight() string { return "🟥" } // Bottom-right corner
func (s *SymbolGraphical) HeaderLeft() string { return "🟧" } // Header left (matches mid junctions)
func (s *SymbolGraphical) HeaderMid() string { return "🟧" } // Header middle (matches mid junctions)
func (s *SymbolGraphical) HeaderRight() string { return "🟧" } // Header right (matches mid junctions)
// Deprecated: SymbolMerger is deprecated; use Glyphs with StyleMerger instead.
// this will be removed soon
type SymbolMerger struct {
row string
column string
center string
corners [9]string // [TL, TM, TR, ML, CenterIdx(unused), MR, BL, BM, BR]
}
// SymbolMerger symbol methods
func (s *SymbolMerger) Name() string { return StyleNameMerger.String() }
func (s *SymbolMerger) Center() string { return s.center } // Main crossing symbol
func (s *SymbolMerger) Row() string { return s.row }
func (s *SymbolMerger) Column() string { return s.column }
func (s *SymbolMerger) TopLeft() string { return s.corners[0] }
func (s *SymbolMerger) TopMid() string { return s.corners[1] } // LevelHeader junction
func (s *SymbolMerger) TopRight() string { return s.corners[2] }
func (s *SymbolMerger) MidLeft() string { return s.corners[3] } // Left junction
func (s *SymbolMerger) MidRight() string { return s.corners[5] } // Right junction
func (s *SymbolMerger) BottomLeft() string { return s.corners[6] }
func (s *SymbolMerger) BottomMid() string { return s.corners[7] } // LevelFooter junction
func (s *SymbolMerger) BottomRight() string { return s.corners[8] }
func (s *SymbolMerger) HeaderLeft() string { return s.MidLeft() }
func (s *SymbolMerger) HeaderMid() string { return s.Center() }
func (s *SymbolMerger) HeaderRight() string { return s.MidRight() }
+237
View File
@@ -0,0 +1,237 @@
// Package tw provides utility functions for text formatting, width calculation, and string manipulation
// specifically tailored for table rendering, including handling ANSI escape codes and Unicode text.
package tw
import (
"math"
"strconv"
"strings"
"unicode"
"unicode/utf8"
"github.com/olekukonko/tablewriter/pkg/twwidth"
// For mathematical operations like ceiling
// For string-to-number conversions
// For string manipulation utilities
// For Unicode character classification
// For UTF-8 rune handling
)
// Title normalizes and uppercases a label string for use in headers.
// It replaces underscores and certain dots with spaces and trims whitespace.
func Title(name string) string {
origLen := len(name)
rs := []rune(name)
for i, r := range rs {
switch r {
case '_':
rs[i] = ' ' // Replace underscores with spaces
case '.':
// Replace dots with spaces unless they are between numeric or space characters
if (i != 0 && !IsIsNumericOrSpace(rs[i-1])) || (i != len(rs)-1 && !IsIsNumericOrSpace(rs[i+1])) {
rs[i] = ' '
}
}
}
name = string(rs)
name = strings.TrimSpace(name)
// If the input was non-empty but trimmed to empty, return a single space
if len(name) == 0 && origLen > 0 {
name = " "
}
// Convert to uppercase for header formatting
return strings.ToUpper(name)
}
// PadCenter centers a string within a specified width using a padding character.
// Extra padding is split between left and right, with slight preference to left if uneven.
func PadCenter(s, pad string, width int) string {
gap := width - twwidth.Width(s)
if gap > 0 {
// Calculate left and right padding; ceil ensures left gets extra if gap is odd
gapLeft := int(math.Ceil(float64(gap) / 2))
gapRight := gap - gapLeft
return strings.Repeat(pad, gapLeft) + s + strings.Repeat(pad, gapRight)
}
// If no padding needed or string is too wide, return as is
return s
}
// PadRight left-aligns a string within a specified width, filling remaining space on the right with padding.
func PadRight(s, pad string, width int) string {
gap := width - twwidth.Width(s)
if gap > 0 {
// Append padding to the right
return s + strings.Repeat(pad, gap)
}
// If no padding needed or string is too wide, return as is
return s
}
// PadLeft right-aligns a string within a specified width, filling remaining space on the left with padding.
func PadLeft(s, pad string, width int) string {
gap := width - twwidth.Width(s)
if gap > 0 {
// Prepend padding to the left
return strings.Repeat(pad, gap) + s
}
// If no padding needed or string is too wide, return as is
return s
}
// Pad aligns a string within a specified width using a padding character.
// It truncates if the string is wider than the target width.
func Pad(s, padChar string, totalWidth int, alignment Align) string {
sDisplayWidth := twwidth.Width(s)
if sDisplayWidth > totalWidth {
return twwidth.Truncate(s, totalWidth) // Only truncate if necessary
}
switch alignment {
case AlignLeft:
return PadRight(s, padChar, totalWidth)
case AlignRight:
return PadLeft(s, padChar, totalWidth)
case AlignCenter:
return PadCenter(s, padChar, totalWidth)
default:
return PadRight(s, padChar, totalWidth)
}
}
// IsIsNumericOrSpace checks if a rune is a digit or space character.
// Used in formatting logic to determine safe character replacements.
func IsIsNumericOrSpace(r rune) bool {
return ('0' <= r && r <= '9') || r == ' '
}
// IsNumeric checks if a string represents a valid integer or floating-point number.
func IsNumeric(s string) bool {
s = strings.TrimSpace(s)
if s == "" {
return false
}
// Try parsing as integer first
if _, err := strconv.Atoi(s); err == nil {
return true
}
// Then try parsing as float
_, err := strconv.ParseFloat(s, 64)
return err == nil
}
// SplitCamelCase splits a camelCase or PascalCase or snake_case string into separate words.
// It detects transitions between uppercase, lowercase, digits, and other characters.
func SplitCamelCase(src string) (entries []string) {
// Validate UTF-8 input; return as single entry if invalid
if !utf8.ValidString(src) {
return []string{src}
}
entries = []string{}
var runes [][]rune
lastClass := 0
class := 0
// Classify each rune into categories: lowercase (1), uppercase (2), digit (3), other (4)
for _, r := range src {
switch {
case unicode.IsLower(r):
class = 1
case unicode.IsUpper(r):
class = 2
case unicode.IsDigit(r):
class = 3
default:
class = 4
}
// Group consecutive runes of the same class together
if class == lastClass {
runes[len(runes)-1] = append(runes[len(runes)-1], r)
} else {
runes = append(runes, []rune{r})
}
lastClass = class
}
// Adjust for cases where an uppercase letter is followed by lowercase (e.g., CamelCase)
for i := 0; i < len(runes)-1; i++ {
if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) {
// Move the last uppercase rune to the next group for proper word splitting
runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...)
runes[i] = runes[i][:len(runes[i])-1]
}
}
// Convert rune groups to strings, excluding empty, underscore or whitespace-only groups
for _, s := range runes {
str := string(s)
if len(s) > 0 && strings.TrimSpace(str) != "" && str != "_" {
entries = append(entries, str)
}
}
return
}
// Or provides a ternary-like operation for strings, returning 'valid' if cond is true, else 'inValid'.
func Or(cond bool, valid, inValid string) string {
if cond {
return valid
}
return inValid
}
// Max returns the greater of two integers.
func Max(a, b int) int {
if a > b {
return a
}
return b
}
// Min returns the smaller of two integers.
func Min(a, b int) int {
if a < b {
return a
}
return b
}
// BreakPoint finds the rune index where the display width of a string first exceeds the specified limit.
// It returns the number of runes if the entire string fits, or 0 if nothing fits.
func BreakPoint(s string, limit int) int {
// If limit is 0 or negative, nothing can fit
if limit <= 0 {
return 0
}
// Empty string has a breakpoint of 0
if s == "" {
return 0
}
currentWidth := 0
runeCount := 0
// Iterate over runes, accumulating display width
for _, r := range s {
runeWidth := twwidth.Width(string(r)) // Calculate width of individual rune
if currentWidth+runeWidth > limit {
// Adding this rune would exceed the limit; breakpoint is before this rune
if currentWidth == 0 {
// First rune is too wide; allow breaking after it if limit > 0
if runeWidth > limit && limit > 0 {
return 1
}
return 0
}
return runeCount
}
currentWidth += runeWidth
runeCount++
}
// Entire string fits within the limit
return runeCount
}
func MakeAlign(l int, align Align) Alignment {
aa := make(Alignment, l)
for i := 0; i < l; i++ {
aa[i] = align
}
return aa
}
+245
View File
@@ -0,0 +1,245 @@
package tw
import (
"fmt"
"sort"
)
// KeyValuePair represents a single key-value pair from a Mapper.
type KeyValuePair[K comparable, V any] struct {
Key K
Value V
}
// Mapper is a generic map type with comparable keys and any value type.
// It provides type-safe operations on maps with additional convenience methods.
type Mapper[K comparable, V any] map[K]V
// NewMapper creates and returns a new initialized Mapper.
func NewMapper[K comparable, V any]() Mapper[K, V] {
return make(Mapper[K, V])
}
// Get returns the value associated with the key.
// If the key doesn't exist or the map is nil, it returns the zero value for the value type.
func (m Mapper[K, V]) Get(key K) V {
if m == nil {
var zero V
return zero
}
return m[key]
}
// OK returns the value associated with the key and a boolean indicating whether the key exists.
func (m Mapper[K, V]) OK(key K) (V, bool) {
if m == nil {
var zero V
return zero, false
}
val, ok := m[key]
return val, ok
}
// Set sets the value for the specified key.
// Does nothing if the map is nil.
func (m Mapper[K, V]) Set(key K, value V) Mapper[K, V] {
if m != nil {
m[key] = value
}
return m
}
// Delete removes the specified key from the map.
// Does nothing if the key doesn't exist or the map is nil.
func (m Mapper[K, V]) Delete(key K) Mapper[K, V] {
if m != nil {
delete(m, key)
}
return m
}
// Has returns true if the key exists in the map, false otherwise.
func (m Mapper[K, V]) Has(key K) bool {
if m == nil {
return false
}
_, exists := m[key]
return exists
}
// Len returns the number of elements in the map.
// Returns 0 if the map is nil.
func (m Mapper[K, V]) Len() int {
if m == nil {
return 0
}
return len(m)
}
// Keys returns a slice containing all keys in the map.
// Returns nil if the map is nil or empty.
func (m Mapper[K, V]) Keys() []K {
if m == nil {
return nil
}
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
func (m Mapper[K, V]) Clear() {
if m == nil {
return
}
for k := range m {
delete(m, k)
}
}
// Values returns a slice containing all values in the map.
// Returns nil if the map is nil or empty.
func (m Mapper[K, V]) Values() []V {
if m == nil {
return nil
}
values := make([]V, 0, len(m))
for _, v := range m {
values = append(values, v)
}
return values
}
// Each iterates over each key-value pair in the map and calls the provided function.
// Does nothing if the map is nil.
func (m Mapper[K, V]) Each(fn func(K, V)) {
for k, v := range m {
fn(k, v)
}
}
// Filter returns a new Mapper containing only the key-value pairs that satisfy the predicate.
func (m Mapper[K, V]) Filter(fn func(K, V) bool) Mapper[K, V] {
result := NewMapper[K, V]()
for k, v := range m {
if fn(k, v) {
result[k] = v
}
}
return result
}
// MapValues returns a new Mapper with the same keys but values transformed by the provided function.
func (m Mapper[K, V]) MapValues(fn func(V) V) Mapper[K, V] {
result := NewMapper[K, V]()
for k, v := range m {
result[k] = fn(v)
}
return result
}
// Clone returns a shallow copy of the Mapper.
func (m Mapper[K, V]) Clone() Mapper[K, V] {
result := NewMapper[K, V]()
for k, v := range m {
result[k] = v
}
return result
}
// Slicer converts the Mapper to a Slicer of key-value pairs.
func (m Mapper[K, V]) Slicer() Slicer[KeyValuePair[K, V]] {
if m == nil {
return nil
}
result := make(Slicer[KeyValuePair[K, V]], 0, len(m))
for k, v := range m {
result = append(result, KeyValuePair[K, V]{Key: k, Value: v})
}
return result
}
func (m Mapper[K, V]) SortedKeys() []K {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
a, b := any(keys[i]), any(keys[j])
switch va := a.(type) {
case int:
if vb, ok := b.(int); ok {
return va < vb
}
case int32:
if vb, ok := b.(int32); ok {
return va < vb
}
case int64:
if vb, ok := b.(int64); ok {
return va < vb
}
case uint:
if vb, ok := b.(uint); ok {
return va < vb
}
case uint64:
if vb, ok := b.(uint64); ok {
return va < vb
}
case float32:
if vb, ok := b.(float32); ok {
return va < vb
}
case float64:
if vb, ok := b.(float64); ok {
return va < vb
}
case string:
if vb, ok := b.(string); ok {
return va < vb
}
}
// fallback to string comparison
return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b)
})
return keys
}
func NewBoolMapper[K comparable](keys ...K) Mapper[K, bool] {
if len(keys) == 0 {
return nil
}
mapper := NewMapper[K, bool]()
for _, key := range keys {
mapper.Set(key, true)
}
return mapper
}
func NewIntMapper[K comparable](keys ...K) Mapper[K, int] {
if len(keys) == 0 {
return nil
}
mapper := NewMapper[K, int]()
for _, key := range keys {
mapper.Set(key, 0)
}
return mapper
}
func NewIdentityMapper[K comparable](keys ...K) Mapper[K, K] {
if len(keys) == 0 {
return nil
}
mapper := NewMapper[K, K]()
for _, key := range keys {
mapper.Set(key, key)
}
return mapper
}
+15
View File
@@ -0,0 +1,15 @@
package tw
// BorderNone defines a border configuration with all sides disabled.
var (
// PaddingNone represents explicitly empty padding (no spacing on any side)
// Equivalent to Padding{Overwrite: true}
PaddingNone = Padding{Left: Empty, Right: Empty, Top: Empty, Bottom: Empty, Overwrite: true}
BorderNone = Border{Left: Off, Right: Off, Top: Off, Bottom: Off}
LinesNone = Lines{ShowTop: Off, ShowBottom: Off, ShowHeaderLine: Off, ShowFooterLine: Off}
SeparatorsNone = Separators{ShowHeader: Off, ShowFooter: Off, BetweenRows: Off, BetweenColumns: Off}
)
// PaddingDefault represents standard single-space padding on left/right
// Equivalent to Padding{Left: " ", Right: " ", Overwrite: true}
var PaddingDefault = Padding{Left: " ", Right: " ", Overwrite: true}
+133
View File
@@ -0,0 +1,133 @@
package tw
import (
"io"
"github.com/olekukonko/ll"
)
// Renderer defines the interface for rendering tables to an io.Writer.
// Implementations must handle headers, rows, footers, and separator lines.
type Renderer interface {
Start(w io.Writer) error
Header(headers [][]string, ctx Formatting) // Renders table header
Row(row []string, ctx Formatting) // Renders a single row
Footer(footers [][]string, ctx Formatting) // Renders table footer
Line(ctx Formatting) // Renders separator line
Config() Rendition // Returns renderer config
Close() error // Gets Rendition form Blueprint
Logger(logger *ll.Logger) // send logger to renderers
}
// Rendition holds the configuration for the default renderer.
type Rendition struct {
Borders Border // Border visibility settings
Symbols Symbols // Symbols used for table drawing
Settings Settings // Rendering behavior settings
Streaming bool
}
// Renditioning has a method to update its rendition.
// Let's define an optional interface for this.
type Renditioning interface {
Rendition(r Rendition)
}
// Formatting encapsulates the complete formatting context for a table row.
// It provides all necessary information to render a row correctly within the table structure.
type Formatting struct {
Row RowContext // Detailed configuration for the row and its cells
Level Level // Hierarchical level (Header, Body, Footer) affecting line drawing
HasFooter bool // Indicates if the table includes a footer section
IsSubRow bool // Marks this as a continuation or padding line in multi-line rows
NormalizedWidths Mapper[int, int]
}
// CellContext defines the properties and formatting state of an individual table cell.
type CellContext struct {
Data string // Content to be displayed in the cell, provided by the caller
Align Align // Text alignment within the cell (Left, Right, Center, Skip)
Padding Padding // Padding characters surrounding the cell content
Width int // Suggested width (often overridden by Row.Widths)
Merge MergeState // Details about cell spanning across rows or columns
}
// MergeState captures how a cell merges across different directions.
type MergeState struct {
Vertical MergeStateOption // Properties for vertical merging (across rows)
Horizontal MergeStateOption // Properties for horizontal merging (across columns)
Hierarchical MergeStateOption // Properties for nested/hierarchical merging
}
// MergeStateOption represents common attributes for merging in a specific direction.
type MergeStateOption struct {
Present bool // True if this merge direction is active
Span int // Number of cells this merge spans
Start bool // True if this cell is the starting point of the merge
End bool // True if this cell is the ending point of the merge
}
// RowContext manages layout properties and relationships for a row and its columns.
// It maintains state about the current row and its neighbors for proper rendering.
type RowContext struct {
Position Position // Section of the table (Header, Row, Footer)
Location Location // Boundary position (First, Middle, End)
Current map[int]CellContext // Cells in this row, indexed by column
Previous map[int]CellContext // Cells from the row above; nil if none
Next map[int]CellContext // Cells from the row below; nil if none
Widths Mapper[int, int] // Computed widths for each column
ColMaxWidths CellWidth // Maximum allowed width per column
}
func (r RowContext) GetCell(col int) CellContext {
return r.Current[col]
}
// Separators controls the visibility of separators in the table.
type Separators struct {
ShowHeader State // Controls header separator visibility
ShowFooter State // Controls footer separator visibility
BetweenRows State // Determines if lines appear between rows
BetweenColumns State // Determines if separators appear between columns
}
// Lines manages the visibility of table boundary lines.
type Lines struct {
ShowTop State // Top border visibility
ShowBottom State // Bottom border visibility
ShowHeaderLine State // Header separator line visibility
ShowFooterLine State // Footer separator line visibility
}
// Settings holds configuration preferences for rendering behavior.
type Settings struct {
Separators Separators // Separator visibility settings
Lines Lines // Line visibility settings
CompactMode State // Reserved for future compact rendering (unused)
// Cushion State
}
// Border defines the visibility states of table borders.
type Border struct {
Left State // Left border visibility
Right State // Right border visibility
Top State // Top border visibility
Bottom State // Bottom border visibility
Overwrite bool
}
type StreamConfig struct {
Enable bool
// StrictColumns, if true, causes Append() to return an error
// in streaming mode if the number of cells in an appended row
// does not match the established number of columns for the stream.
// If false (default), rows with mismatched column counts will be
// padded or truncated with a warning log.
StrictColumns bool
// Deprecated: Use top-level Config.Widths for streaming width control.
// This field will be removed in a future version. It will be respected if
// Config.Widths is not set and this field is.
Widths CellWidth
}
+134
View File
@@ -0,0 +1,134 @@
package tw
import "slices"
// Slicer is a generic slice type that provides additional methods for slice manipulation.
type Slicer[T any] []T
// NewSlicer creates and returns a new initialized Slicer.
func NewSlicer[T any]() Slicer[T] {
return make(Slicer[T], 0)
}
// Get returns the element at the specified index.
// Returns the zero value if the index is out of bounds or the slice is nil.
func (s Slicer[T]) Get(index int) T {
if s == nil || index < 0 || index >= len(s) {
var zero T
return zero
}
return s[index]
}
// GetOK returns the element at the specified index and a boolean indicating whether the index was valid.
func (s Slicer[T]) GetOK(index int) (T, bool) {
if s == nil || index < 0 || index >= len(s) {
var zero T
return zero, false
}
return s[index], true
}
// Append appends elements to the slice and returns the new slice.
func (s Slicer[T]) Append(elements ...T) Slicer[T] {
return append(s, elements...)
}
// Prepend adds elements to the beginning of the slice and returns the new slice.
func (s Slicer[T]) Prepend(elements ...T) Slicer[T] {
return append(elements, s...)
}
// Len returns the number of elements in the slice.
// Returns 0 if the slice is nil.
func (s Slicer[T]) Len() int {
if s == nil {
return 0
}
return len(s)
}
// IsEmpty returns true if the slice is nil or has zero elements.
func (s Slicer[T]) IsEmpty() bool {
return s.Len() == 0
}
// Has returns true if the index exists in the slice.
func (s Slicer[T]) Has(index int) bool {
return index >= 0 && index < s.Len()
}
// First returns the first element of the slice, or the zero value if empty.
func (s Slicer[T]) First() T {
return s.Get(0)
}
// Last returns the last element of the slice, or the zero value if empty.
func (s Slicer[T]) Last() T {
return s.Get(s.Len() - 1)
}
// Each iterates over each element in the slice and calls the provided function.
// Does nothing if the slice is nil.
func (s Slicer[T]) Each(fn func(T)) {
for _, v := range s {
fn(v)
}
}
// Filter returns a new Slicer containing only elements that satisfy the predicate.
func (s Slicer[T]) Filter(fn func(T) bool) Slicer[T] {
result := NewSlicer[T]()
for _, v := range s {
if fn(v) {
result = result.Append(v)
}
}
return result
}
// Map returns a new Slicer with each element transformed by the provided function.
func (s Slicer[T]) Map(fn func(T) T) Slicer[T] {
result := NewSlicer[T]()
for _, v := range s {
result = result.Append(fn(v))
}
return result
}
// Contains returns true if the slice contains an element that satisfies the predicate.
func (s Slicer[T]) Contains(fn func(T) bool) bool {
return slices.ContainsFunc(s, fn)
}
// Find returns the first element that satisfies the predicate, along with a boolean indicating if it was found.
func (s Slicer[T]) Find(fn func(T) bool) (T, bool) {
for _, v := range s {
if fn(v) {
return v, true
}
}
var zero T
return zero, false
}
// Clone returns a shallow copy of the Slicer.
func (s Slicer[T]) Clone() Slicer[T] {
result := NewSlicer[T]()
if s != nil {
result = append(result, s...)
}
return result
}
// SlicerToMapper converts a Slicer of KeyValuePair to a Mapper.
func SlicerToMapper[K comparable, V any](s Slicer[KeyValuePair[K, V]]) Mapper[K, V] {
result := make(Mapper[K, V])
if s == nil {
return result
}
for _, pair := range s {
result[pair.Key] = pair.Value
}
return result
}
+51
View File
@@ -0,0 +1,51 @@
package tw
// State represents an on/off state
type State int
// Public: Methods for State type
// Enabled checks if the state is on
func (o State) Enabled() bool { return o == Success }
// Default checks if the state is unknown
func (o State) Default() bool { return o == Unknown }
// Disabled checks if the state is off
func (o State) Disabled() bool { return o == Fail }
// Toggle switches the state between on and off
func (o State) Toggle() State {
if o == Fail {
return Success
}
return Fail
}
// Cond executes a condition if the state is enabled
func (o State) Cond(c func() bool) bool {
if o.Enabled() {
return c()
}
return false
}
// Or returns this state if enabled, else the provided state
func (o State) Or(c State) State {
if o.Enabled() {
return o
}
return c
}
// String returns the string representation of the state
func (o State) String() string {
if o.Enabled() {
return "on"
}
if o.Disabled() {
return "off"
}
return "undefined"
}
File diff suppressed because it is too large Load Diff
+109
View File
@@ -0,0 +1,109 @@
package tw
// Operation Status Constants
// Used to indicate the success or failure of operations
const (
Pending = 0 // Operation failed
Fail = -1 // Operation failed
Success = 1 // Operation succeeded
MinimumColumnWidth = 8
DefaultCacheStringCapacity = 10 * 1024 // 10 KB
)
const (
Empty = ""
Skip = ""
Space = " "
NewLine = "\n"
Column = ":"
Dash = "-"
)
// Feature State Constants
// Represents enabled/disabled states for features
const (
Unknown State = Pending // Feature is enabled
On State = Success // Feature is enabled
Off State = Fail // Feature is disabled
)
// Table Alignment Constants
// Defines text alignment options for table content
const (
AlignNone Align = "none" // Center-aligned text
AlignCenter Align = "center" // Center-aligned text
AlignRight Align = "right" // Right-aligned text
AlignLeft Align = "left" // Left-aligned text
AlignDefault = AlignLeft // Left-aligned text
)
const (
Header Position = "header" // Table header section
Row Position = "row" // Table row section
Footer Position = "footer" // Table footer section
)
const (
LevelHeader Level = iota // Topmost line position
LevelBody // LevelBody line position
LevelFooter // LevelFooter line position
)
const (
LocationFirst Location = "first" // Topmost line position
LocationMiddle Location = "middle" // LevelBody line position
LocationEnd Location = "end" // LevelFooter line position
)
const (
SectionHeader = "header"
SectionRow = "row"
SectionFooter = "footer"
)
// Text Wrapping Constants
// Defines text wrapping behavior in table cells
const (
WrapNone = iota // No wrapping
WrapNormal // Standard word wrapping
WrapTruncate // Truncate text with ellipsis
WrapBreak // Break words to fit
)
// Cell Merge Constants
// Specifies cell merging behavior in tables
const (
MergeNone = iota // No merging
MergeVertical // Merge cells vertically
MergeHorizontal // Merge cells horizontally
MergeBoth // Merge both vertically and horizontally
MergeHierarchical // Hierarchical merging
)
// Special Character Constants
// Defines special characters used in formatting
const (
CharEllipsis = "…" // Ellipsis character for truncation
CharBreak = "↩" // Break character for wrapping
)
type Spot int
const (
SpotNone Spot = iota
SpotTopLeft
SpotTopCenter
SpotTopRight
SpotBottomLeft
SpotBottomCenter // Default for legacy SetCaption
SpotBottomRight
SpotLeftTop
SpotLeftCenter
SpotLeftBottom
SpotRightTop
SpotRightCenter
SpotRIghtBottom
)
+244
View File
@@ -0,0 +1,244 @@
// Package tw defines types and constants for table formatting and configuration,
// including validation logic for various table properties.
package tw
import (
"bytes"
"io"
"strconv"
"strings"
"github.com/olekukonko/errors"
) // Custom error handling library
// Position defines where formatting applies in the table (e.g., header, footer, or rows).
type Position string
// Validate checks if the Position is one of the allowed values: Header, Footer, or Row.
func (pos Position) Validate() error {
switch pos {
case Header, Footer, Row:
return nil // Valid position
}
// Return an error for any unrecognized position
return errors.New("invalid position")
}
// Filter defines a function type for processing cell content.
// It takes a slice of strings (representing cell data) and returns a processed slice.
type Filter func([]string) []string
// Formatter defines an interface for types that can format themselves into a string.
// Used for custom formatting of table cell content.
type Formatter interface {
Format() string // Returns the formatted string representation
}
// Counter defines an interface that combines io.Writer with a method to retrieve a total.
// This is used by the WithCounter option to allow for counting lines, bytes, etc.
type Counter interface {
io.Writer // It must be a writer to be used in io.MultiWriter.
Total() int
}
// Align specifies the text alignment within a table cell.
type Align string
// Validate checks if the Align is one of the allowed values: None, Center, Left, or Right.
func (a Align) Validate() error {
switch a {
case AlignNone, AlignCenter, AlignLeft, AlignRight:
return nil // Valid alignment
}
// Return an error for any unrecognized alignment
return errors.New("invalid align")
}
type Alignment []Align
func (a Alignment) String() string {
var str strings.Builder
for i, a := range a {
if i > 0 {
str.WriteString("; ")
}
str.WriteString(strconv.Itoa(i))
str.WriteString("=")
str.WriteString(string(a))
}
return str.String()
}
func (a Alignment) Add(aligns ...Align) Alignment {
aa := make(Alignment, len(aligns))
copy(aa, aligns)
return aa
}
func (a Alignment) Set(col int, align Align) Alignment {
if col >= 0 && col < len(a) {
a[col] = align
}
return a
}
// Copy creates a new independent copy of the Alignment
func (a Alignment) Copy() Alignment {
aa := make(Alignment, len(a))
copy(aa, a)
return aa
}
// Level indicates the vertical position of a line in the table (e.g., header, body, or footer).
type Level int
// Validate checks if the Level is one of the allowed values: Header, Body, or Footer.
func (l Level) Validate() error {
switch l {
case LevelHeader, LevelBody, LevelFooter:
return nil // Valid level
}
// Return an error for any unrecognized level
return errors.New("invalid level")
}
// Location specifies the horizontal position of a cell or column within a table row.
type Location string
// Validate checks if the Location is one of the allowed values: First, Middle, or End.
func (l Location) Validate() error {
switch l {
case LocationFirst, LocationMiddle, LocationEnd:
return nil // Valid location
}
// Return an error for any unrecognized location
return errors.New("invalid location")
}
type Caption struct {
Text string
Spot Spot
Align Align
Width int
}
func (c Caption) WithText(text string) Caption {
c.Text = text
return c
}
func (c Caption) WithSpot(spot Spot) Caption {
c.Spot = spot
return c
}
func (c Caption) WithAlign(align Align) Caption {
c.Align = align
return c
}
func (c Caption) WithWidth(width int) Caption {
c.Width = width
return c
}
type Control struct {
Hide State
}
// Compact configures compact width optimization for merged cells.
type Compact struct {
Merge State // Merge enables compact width calculation during cell merging, optimizing space allocation.
}
// Struct holds settings for struct-based operations like AutoHeader.
type Struct struct {
// AutoHeader automatically extracts and sets headers from struct fields when Bulk is called with a slice of structs.
// Uses JSON tags if present, falls back to field names (title-cased). Skips unexported or json:"-" fields.
// Enabled by default for convenience.
AutoHeader State
// Tags is a priority-ordered list of struct tag keys to check for header names.
// The first tag found on a field will be used. Defaults to ["json", "db"].
Tags []string
}
// Behavior defines settings that control table rendering behaviors, such as column visibility and content formatting.
type Behavior struct {
AutoHide State // AutoHide determines whether empty columns are hidden. Ignored in streaming mode.
TrimSpace State // TrimSpace determines trimming of leading and trailing spaces from cell content.
TrimLine State // TrimLine determines whether empty visual lines within a cell are collapsed.
TrimTab State // TrimTab determines trimming of leading and trailing tabs from cell content.
Header Control // Header specifies control settings for the table header.
Footer Control // Footer specifies control settings for the table footer.
// Compact enables optimized width calculation for merged cells, such as in horizontal merges,
// by systematically determining the most efficient width instead of scaling by the number of columns.
Compact Compact
// Structs contains settings for how struct data is processed.
Structs Struct
}
// Padding defines the spacing characters around cell content in all four directions.
// A zero-value Padding struct will use the table's default padding unless Overwrite is true.
type Padding struct {
Left string
Right string
Top string
Bottom string
// Overwrite forces tablewriter to use this padding configuration exactly as specified,
// even when empty. When false (default), empty Padding fields will inherit defaults.
//
// For explicit no-padding, use the PaddingNone constant instead of setting Overwrite.
Overwrite bool
}
// Common padding configurations for convenience
// Equals reports whether two Padding configurations are identical in all fields.
// This includes comparing the Overwrite flag as part of the equality check.
func (p Padding) Equals(padding Padding) bool {
return p.Left == padding.Left &&
p.Right == padding.Right &&
p.Top == padding.Top &&
p.Bottom == padding.Bottom &&
p.Overwrite == padding.Overwrite
}
// Empty reports whether all padding strings are empty (all fields == "").
// Note that an Empty padding may still take effect if Overwrite is true.
func (p Padding) Empty() bool {
return p.Left == "" && p.Right == "" && p.Top == "" && p.Bottom == ""
}
// Paddable reports whether this Padding configuration should override existing padding.
// Returns true if either:
// - Any padding string is non-empty (!p.Empty())
// - Overwrite flag is true (even with all strings empty)
//
// This is used internally during configuration merging to determine whether to
// apply the padding settings.
func (p Padding) Paddable() bool {
return !p.Empty() || p.Overwrite
}
// LineCounter is the default implementation of the Counter interface.
// It counts the number of newline characters written to it.
type LineCounter struct {
count int
}
// Write implements the io.Writer interface, counting newlines in the input.
// It uses a pointer receiver to modify the internal count.
func (lc *LineCounter) Write(p []byte) (n int, err error) {
lc.count += bytes.Count(p, []byte{'\n'})
return len(p), nil
}
// Total implements the Counter interface, returning the final count.
func (lc *LineCounter) Total() int {
return lc.count
}