Initial QSfera import
This commit is contained in:
+991
@@ -0,0 +1,991 @@
|
||||
package tablewriter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/olekukonko/ll"
|
||||
"github.com/olekukonko/tablewriter/pkg/twcache"
|
||||
"github.com/olekukonko/tablewriter/pkg/twwidth"
|
||||
"github.com/olekukonko/tablewriter/tw"
|
||||
)
|
||||
|
||||
// Option defines a function type for configuring a Table instance.
|
||||
type Option func(target *Table)
|
||||
|
||||
// WithAutoHide enables or disables automatic hiding of columns with empty data rows.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithAutoHide(state tw.State) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Behavior.AutoHide = state
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithAutoHide applied to Table: %v", state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithColumnMax sets a global maximum column width for the table in streaming mode.
|
||||
// Negative values are ignored, and the change is logged if debugging is enabled.
|
||||
func WithColumnMax(width int) Option {
|
||||
return func(target *Table) {
|
||||
if width < 0 {
|
||||
return
|
||||
}
|
||||
target.config.Widths.Global = width
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithColumnMax applied to Table: %v", width)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxWidth sets a global maximum table width for the table.
|
||||
// Negative values are ignored, and the change is logged if debugging is enabled.
|
||||
func WithMaxWidth(width int) Option {
|
||||
return func(target *Table) {
|
||||
if width < 0 {
|
||||
return
|
||||
}
|
||||
target.config.MaxWidth = width
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithTableMax applied to Table: %v", width)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithWidths sets per-column widths for the table.
|
||||
// Negative widths are removed, and the change is logged if debugging is enabled.
|
||||
func WithWidths(width tw.CellWidth) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Widths = width
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithColumnWidths applied to Table: %v", width)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithColumnWidths sets per-column widths for the table.
|
||||
// Negative widths are removed, and the change is logged if debugging is enabled.
|
||||
func WithColumnWidths(widths tw.Mapper[int, int]) Option {
|
||||
return func(target *Table) {
|
||||
for k, v := range widths {
|
||||
if v < 0 {
|
||||
delete(widths, k)
|
||||
}
|
||||
}
|
||||
target.config.Widths.PerColumn = widths
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithColumnWidths applied to Table: %v", widths)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithConfig applies a custom configuration to the table by merging it with the default configuration.
|
||||
func WithConfig(cfg Config) Option {
|
||||
return func(target *Table) {
|
||||
target.config = mergeConfig(defaultConfig(), cfg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithDebug enables or disables debug logging and adjusts the logger level accordingly.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithDebug(debug bool) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Debug = debug
|
||||
}
|
||||
}
|
||||
|
||||
// WithFooter sets the table footers by calling the Footer method.
|
||||
func WithFooter(footers []string) Option {
|
||||
return func(target *Table) {
|
||||
target.Footer(footers)
|
||||
}
|
||||
}
|
||||
|
||||
// WithFooterConfig applies a full footer configuration to the table.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithFooterConfig(config tw.CellConfig) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Footer = config
|
||||
if target.logger != nil {
|
||||
target.logger.Debug("Option: WithFooterConfig applied to Table.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithFooterAlignmentConfig applies a footer alignment configuration to the table.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithFooterAlignmentConfig(alignment tw.CellAlignment) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Footer.Alignment = alignment
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithFooterAlignmentConfig applied to Table: %+v", alignment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: Use a ConfigBuilder with .Footer().CellMerging().WithMode(...) instead.
|
||||
// This option will be removed in a future version.
|
||||
func WithFooterMergeMode(mergeMode int) Option {
|
||||
return func(target *Table) {
|
||||
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
||||
return
|
||||
}
|
||||
target.config.Footer.Merging.Mode = mergeMode
|
||||
target.config.Footer.Formatting.MergeMode = mergeMode
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithFooterMergeMode applied to Table: %v", mergeMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithFooterAutoWrap sets the wrapping behavior for footer cells.
|
||||
// Invalid wrap modes are ignored, and the change is logged if debugging is enabled.
|
||||
func WithFooterAutoWrap(wrap int) Option {
|
||||
return func(target *Table) {
|
||||
if wrap < tw.WrapNone || wrap > tw.WrapBreak {
|
||||
return
|
||||
}
|
||||
target.config.Footer.Formatting.AutoWrap = wrap
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithFooterAutoWrap applied to Table: %v", wrap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithFooterFilter sets the filter configuration for footer cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithFooterFilter(filter tw.CellFilter) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Footer.Filter = filter
|
||||
if target.logger != nil {
|
||||
target.logger.Debug("Option: WithFooterFilter applied to Table.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithFooterCallbacks sets the callback configuration for footer cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithFooterCallbacks(callbacks tw.CellCallbacks) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Footer.Callbacks = callbacks
|
||||
if target.logger != nil {
|
||||
target.logger.Debug("Option: WithFooterCallbacks applied to Table.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithFooterPaddingPerColumn sets per-column padding for footer cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithFooterPaddingPerColumn(padding []tw.Padding) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Footer.Padding.PerColumn = padding
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithFooterPaddingPerColumn applied to Table: %+v", padding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithFooterMaxWidth sets the maximum content width for footer cells.
|
||||
// Negative values are ignored, and the change is logged if debugging is enabled.
|
||||
func WithFooterMaxWidth(maxWidth int) Option {
|
||||
return func(target *Table) {
|
||||
if maxWidth < 0 {
|
||||
return
|
||||
}
|
||||
target.config.Footer.ColMaxWidths.Global = maxWidth
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithFooterMaxWidth applied to Table: %v", maxWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeader sets the table headers by calling the Header method.
|
||||
func WithHeader(headers []string) Option {
|
||||
return func(target *Table) {
|
||||
target.Header(headers)
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderAlignment sets the text alignment for header cells.
|
||||
// Invalid alignments are ignored, and the change is logged if debugging is enabled.
|
||||
func WithHeaderAlignment(align tw.Align) Option {
|
||||
return func(target *Table) {
|
||||
if align != tw.AlignLeft && align != tw.AlignRight && align != tw.AlignCenter && align != tw.AlignNone {
|
||||
return
|
||||
}
|
||||
target.config.Header.Alignment.Global = align
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithHeaderAlignment applied to Table: %v", align)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderAutoWrap sets the wrapping behavior for header cells.
|
||||
// Invalid wrap modes are ignored, and the change is logged if debugging is enabled.
|
||||
func WithHeaderAutoWrap(wrap int) Option {
|
||||
return func(target *Table) {
|
||||
if wrap < tw.WrapNone || wrap > tw.WrapBreak {
|
||||
return
|
||||
}
|
||||
target.config.Header.Formatting.AutoWrap = wrap
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithHeaderAutoWrap applied to Table: %v", wrap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: Use a ConfigBuilder with .Header().CellMerging().WithMode(...) instead.
|
||||
// This option will be removed in a future version.
|
||||
func WithHeaderMergeMode(mergeMode int) Option {
|
||||
return func(target *Table) {
|
||||
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
||||
return
|
||||
}
|
||||
target.config.Header.Merging.Mode = mergeMode
|
||||
target.config.Header.Formatting.MergeMode = mergeMode
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithHeaderMergeMode applied to Table: %v", mergeMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderFilter sets the filter configuration for header cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithHeaderFilter(filter tw.CellFilter) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Header.Filter = filter
|
||||
if target.logger != nil {
|
||||
target.logger.Debug("Option: WithHeaderFilter applied to Table.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderCallbacks sets the callback configuration for header cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithHeaderCallbacks(callbacks tw.CellCallbacks) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Header.Callbacks = callbacks
|
||||
if target.logger != nil {
|
||||
target.logger.Debug("Option: WithHeaderCallbacks applied to Table.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderPaddingPerColumn sets per-column padding for header cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithHeaderPaddingPerColumn(padding []tw.Padding) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Header.Padding.PerColumn = padding
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithHeaderPaddingPerColumn applied to Table: %+v", padding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderMaxWidth sets the maximum content width for header cells.
|
||||
// Negative values are ignored, and the change is logged if debugging is enabled.
|
||||
func WithHeaderMaxWidth(maxWidth int) Option {
|
||||
return func(target *Table) {
|
||||
if maxWidth < 0 {
|
||||
return
|
||||
}
|
||||
target.config.Header.ColMaxWidths.Global = maxWidth
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithHeaderMaxWidth applied to Table: %v", maxWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRowAlignment sets the text alignment for row cells.
|
||||
// Invalid alignments are ignored, and the change is logged if debugging is enabled.
|
||||
func WithRowAlignment(align tw.Align) Option {
|
||||
return func(target *Table) {
|
||||
if err := align.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
target.config.Row.Alignment.Global = align
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithRowAlignment applied to Table: %v", align)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRowAutoWrap sets the wrapping behavior for row cells.
|
||||
// Invalid wrap modes are ignored, and the change is logged if debugging is enabled.
|
||||
func WithRowAutoWrap(wrap int) Option {
|
||||
return func(target *Table) {
|
||||
if wrap < tw.WrapNone || wrap > tw.WrapBreak {
|
||||
return
|
||||
}
|
||||
target.config.Row.Formatting.AutoWrap = wrap
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithRowAutoWrap applied to Table: %v", wrap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: Use a ConfigBuilder with .Row().CellMerging().WithMode(...) instead.
|
||||
// This option will be removed in a future version.
|
||||
func WithRowMergeMode(mergeMode int) Option {
|
||||
return func(target *Table) {
|
||||
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
||||
return
|
||||
}
|
||||
target.config.Row.Merging.Mode = mergeMode
|
||||
target.config.Row.Formatting.MergeMode = mergeMode
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithRowMergeMode applied to Table: %v", mergeMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRowFilter sets the filter configuration for row cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithRowFilter(filter tw.CellFilter) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Row.Filter = filter
|
||||
if target.logger != nil {
|
||||
target.logger.Debug("Option: WithRowFilter applied to Table.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRowCallbacks sets the callback configuration for row cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithRowCallbacks(callbacks tw.CellCallbacks) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Row.Callbacks = callbacks
|
||||
if target.logger != nil {
|
||||
target.logger.Debug("Option: WithRowCallbacks applied to Table.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRowPaddingPerColumn sets per-column padding for row cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithRowPaddingPerColumn(padding []tw.Padding) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Row.Padding.PerColumn = padding
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithRowPaddingPerColumn applied to Table: %+v", padding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderAlignmentConfig applies a header alignment configuration to the table.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithHeaderAlignmentConfig(alignment tw.CellAlignment) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Header.Alignment = alignment
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithHeaderAlignmentConfig applied to Table: %+v", alignment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderConfig applies a full header configuration to the table.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithHeaderConfig(config tw.CellConfig) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Header = config
|
||||
if target.logger != nil {
|
||||
target.logger.Debug("Option: WithHeaderConfig applied to Table.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger sets a custom logger for the table and updates the renderer if present.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithLogger(logger *ll.Logger) Option {
|
||||
return func(target *Table) {
|
||||
target.logger = logger
|
||||
if target.logger != nil {
|
||||
target.logger.Debug("Option: WithLogger applied to Table.")
|
||||
if target.renderer != nil {
|
||||
target.renderer.Logger(target.logger)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRenderer sets a custom renderer for the table and attaches the logger if present.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithRenderer(f tw.Renderer) Option {
|
||||
return func(target *Table) {
|
||||
target.renderer = f
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithRenderer applied to Table: %T", f)
|
||||
f.Logger(target.logger)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRowConfig applies a full row configuration to the table.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithRowConfig(config tw.CellConfig) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Row = config
|
||||
if target.logger != nil {
|
||||
target.logger.Debug("Option: WithRowConfig applied to Table.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRowAlignmentConfig applies a row alignment configuration to the table.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithRowAlignmentConfig(alignment tw.CellAlignment) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Row.Alignment = alignment
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithRowAlignmentConfig applied to Table: %+v", alignment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRowMaxWidth sets the maximum content width for row cells.
|
||||
// Negative values are ignored, and the change is logged if debugging is enabled.
|
||||
func WithRowMaxWidth(maxWidth int) Option {
|
||||
return func(target *Table) {
|
||||
if maxWidth < 0 {
|
||||
return
|
||||
}
|
||||
target.config.Row.ColMaxWidths.Global = maxWidth
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithRowMaxWidth applied to Table: %v", maxWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithStreaming applies a streaming configuration to the table by merging it with the existing configuration.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithStreaming(c tw.StreamConfig) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Stream = mergeStreamConfig(target.config.Stream, c)
|
||||
if target.logger != nil {
|
||||
target.logger.Debug("Option: WithStreaming applied to Table.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithStringer sets a custom stringer function for converting row data and clears the stringer cache.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithStringer(stringer interface{}) Option {
|
||||
return func(t *Table) {
|
||||
t.stringer = stringer
|
||||
t.stringerCache = twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity)
|
||||
if t.logger != nil {
|
||||
t.logger.Debug("Stringer updated, cache cleared")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithStringerCache enables the default LRU caching for the stringer function.
|
||||
// It initializes the cache with a default capacity if one does not already exist.
|
||||
func WithStringerCache() Option {
|
||||
return func(t *Table) {
|
||||
// Initialize default cache if strictly necessary (nil),
|
||||
// or if you want to ensure the default implementation is used.
|
||||
if t.stringerCache == nil {
|
||||
// NewLRU returns (Instance, error). We ignore the error here assuming capacity > 0.
|
||||
cache := twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity)
|
||||
t.stringerCache = cache
|
||||
}
|
||||
|
||||
if t.logger != nil {
|
||||
t.logger.Debug("Option: WithStringerCache enabled (Default LRU)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithStringerCacheCustom enables caching for the stringer function using a specific implementation.
|
||||
// Passing nil disables caching entirely.
|
||||
func WithStringerCacheCustom(cache twcache.Cache[reflect.Type, reflect.Value]) Option {
|
||||
return func(t *Table) {
|
||||
if cache == nil {
|
||||
t.stringerCache = nil
|
||||
if t.logger != nil {
|
||||
t.logger.Debug("Option: WithStringerCacheCustom called with nil (Caching Disabled)")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Set the custom cache and enable the flag
|
||||
t.stringerCache = cache
|
||||
|
||||
if t.logger != nil {
|
||||
t.logger.Debug("Option: WithStringerCacheCustom enabled")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithTrimSpace sets whether leading and trailing spaces are automatically trimmed.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithTrimSpace(state tw.State) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Behavior.TrimSpace = state
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithTrimSpace applied to Table: %v", state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithTrimTab sets whether leading and trailing tab characters are automatically trimmed.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithTrimTab(state tw.State) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Behavior.TrimTab = state
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithTrimTab applied to Table: %v", state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithTrimLine sets whether empty visual lines within a cell are trimmed.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithTrimLine(state tw.State) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Behavior.TrimLine = state
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithTrimLine applied to Table: %v", state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderAutoFormat enables or disables automatic formatting for header cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithHeaderAutoFormat(state tw.State) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Header.Formatting.AutoFormat = state
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithHeaderAutoFormat applied to Table: %v", state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithFooterAutoFormat enables or disables automatic formatting for footer cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithFooterAutoFormat(state tw.State) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Footer.Formatting.AutoFormat = state
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithFooterAutoFormat applied to Table: %v", state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRowAutoFormat enables or disables automatic formatting for row cells.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithRowAutoFormat(state tw.State) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Row.Formatting.AutoFormat = state
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithRowAutoFormat applied to Table: %v", state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderControl sets the control behavior for the table header.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithHeaderControl(control tw.Control) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Behavior.Header = control
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithHeaderControl applied to Table: %v", control)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithFooterControl sets the control behavior for the table footer.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithFooterControl(control tw.Control) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Behavior.Footer = control
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithFooterControl applied to Table: %v", control)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithAlignment sets the default column alignment for the header, rows, and footer.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithAlignment(alignment tw.Alignment) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Header.Alignment.PerColumn = alignment
|
||||
target.config.Row.Alignment.PerColumn = alignment
|
||||
target.config.Footer.Alignment.PerColumn = alignment
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithAlignment applied to Table: %+v", alignment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithBehavior applies a behavior configuration to the table.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithBehavior(behavior tw.Behavior) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Behavior = behavior
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithBehavior applied to Table: %+v", behavior)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithPadding sets the global padding for the header, rows, and footer.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithPadding(padding tw.Padding) Option {
|
||||
return func(target *Table) {
|
||||
target.config.Header.Padding.Global = padding
|
||||
target.config.Row.Padding.Global = padding
|
||||
target.config.Footer.Padding.Global = padding
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithPadding applied to Table: %+v", padding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRendition allows updating the active renderer's rendition configuration
|
||||
// by merging the provided rendition.
|
||||
// If the renderer does not implement tw.Renditioning, a warning is logged.
|
||||
// Logs the change if debugging is enabled.
|
||||
func WithRendition(rendition tw.Rendition) Option {
|
||||
return func(target *Table) {
|
||||
if target.renderer == nil {
|
||||
if target.logger != nil {
|
||||
target.logger.Warn("Option: WithRendition: No renderer set on table.")
|
||||
}
|
||||
return
|
||||
}
|
||||
if ru, ok := target.renderer.(tw.Renditioning); ok {
|
||||
ru.Rendition(rendition)
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithRendition: Applied to renderer via Renditioning.SetRendition(): %+v", rendition)
|
||||
}
|
||||
} else if target.logger != nil {
|
||||
target.logger.Warnf("Option: WithRendition: Current renderer type %T does not implement tw.Renditioning. Rendition may not be applied as expected.", target.renderer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithEastAsian configures the global East Asian width calculation setting.
|
||||
// - state=tw.On: Enables East Asian width calculations. CJK and ambiguous characters
|
||||
// are typically measured as double width.
|
||||
// - state=tw.Off: Disables East Asian width calculations. Characters are generally
|
||||
// measured as single width, subject to Unicode standards.
|
||||
//
|
||||
// This setting affects all subsequent display width calculations using the twdw package.
|
||||
func WithEastAsian(state tw.State) Option {
|
||||
return func(target *Table) {
|
||||
if state.Enabled() {
|
||||
twwidth.SetEastAsian(true)
|
||||
}
|
||||
if state.Disabled() {
|
||||
twwidth.SetEastAsian(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithSymbols sets the symbols used for drawing table borders and separators.
|
||||
// The symbols are applied to the table's renderer configuration, if a renderer is set.
|
||||
// If no renderer is set (target.renderer is nil), this option has no effect. .
|
||||
func WithSymbols(symbols tw.Symbols) Option {
|
||||
return func(target *Table) {
|
||||
if target.renderer != nil {
|
||||
cfg := target.renderer.Config()
|
||||
cfg.Symbols = symbols
|
||||
|
||||
if ru, ok := target.renderer.(tw.Renditioning); ok {
|
||||
ru.Rendition(cfg)
|
||||
if target.logger != nil {
|
||||
target.logger.Debugf("Option: WithRendition: Applied to renderer via Renditioning.SetRendition(): %+v", cfg)
|
||||
}
|
||||
} else if target.logger != nil {
|
||||
target.logger.Warnf("Option: WithRendition: Current renderer type %T does not implement tw.Renditioning. Rendition may not be applied as expected.", target.renderer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithCounters enables line counting by wrapping the table's writer.
|
||||
// If a custom counter (that implements tw.Counter) is provided, it will be used.
|
||||
// If the provided counter is nil, a default tw.LineCounter will be used.
|
||||
// The final count can be retrieved via the table.Lines() method after Render() is called.
|
||||
func WithCounters(counters ...tw.Counter) Option {
|
||||
return func(target *Table) {
|
||||
// Iterate through the provided counters and add any non-nil ones.
|
||||
for _, c := range counters {
|
||||
if c != nil {
|
||||
target.counters = append(target.counters, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithLineCounter enables the default line counter.
|
||||
// A new instance of tw.LineCounter is added to the table's list of counters.
|
||||
// The total count can be retrieved via the table.Lines() method after Render() is called.
|
||||
func WithLineCounter() Option {
|
||||
return func(target *Table) {
|
||||
// Important: Create a new instance so tables don't share counters.
|
||||
target.counters = append(target.counters, &tw.LineCounter{})
|
||||
}
|
||||
}
|
||||
|
||||
// defaultConfig returns a default Config with sensible settings for headers, rows, footers, and behavior.
|
||||
func defaultConfig() Config {
|
||||
return Config{
|
||||
MaxWidth: 0,
|
||||
Header: tw.CellConfig{
|
||||
Formatting: tw.CellFormatting{
|
||||
AutoWrap: tw.WrapTruncate,
|
||||
AutoFormat: tw.On,
|
||||
MergeMode: tw.MergeNone,
|
||||
},
|
||||
Merging: tw.CellMerging{
|
||||
Mode: tw.MergeNone,
|
||||
},
|
||||
Padding: tw.CellPadding{
|
||||
Global: tw.PaddingDefault,
|
||||
},
|
||||
Alignment: tw.CellAlignment{
|
||||
Global: tw.AlignCenter,
|
||||
PerColumn: []tw.Align{},
|
||||
},
|
||||
},
|
||||
Row: tw.CellConfig{
|
||||
Formatting: tw.CellFormatting{
|
||||
AutoWrap: tw.WrapNormal,
|
||||
AutoFormat: tw.Off,
|
||||
MergeMode: tw.MergeNone,
|
||||
},
|
||||
Merging: tw.CellMerging{
|
||||
Mode: tw.MergeNone,
|
||||
},
|
||||
Padding: tw.CellPadding{
|
||||
Global: tw.PaddingDefault,
|
||||
},
|
||||
Alignment: tw.CellAlignment{
|
||||
Global: tw.AlignLeft,
|
||||
PerColumn: []tw.Align{},
|
||||
},
|
||||
},
|
||||
Footer: tw.CellConfig{
|
||||
Formatting: tw.CellFormatting{
|
||||
AutoWrap: tw.WrapNormal,
|
||||
AutoFormat: tw.Off,
|
||||
MergeMode: tw.MergeNone,
|
||||
},
|
||||
Merging: tw.CellMerging{
|
||||
Mode: tw.MergeNone,
|
||||
},
|
||||
Padding: tw.CellPadding{
|
||||
Global: tw.PaddingDefault,
|
||||
},
|
||||
Alignment: tw.CellAlignment{
|
||||
Global: tw.AlignRight,
|
||||
PerColumn: []tw.Align{},
|
||||
},
|
||||
},
|
||||
Stream: tw.StreamConfig{
|
||||
Enable: false,
|
||||
StrictColumns: false,
|
||||
},
|
||||
Debug: false,
|
||||
Behavior: tw.Behavior{
|
||||
AutoHide: tw.Off,
|
||||
TrimSpace: tw.On,
|
||||
TrimTab: tw.On,
|
||||
TrimLine: tw.On,
|
||||
Structs: tw.Struct{
|
||||
AutoHeader: tw.Off,
|
||||
Tags: []string{"json", "db"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// mergeCellConfig merges a source CellConfig into a destination CellConfig, prioritizing non-default source values.
|
||||
// It handles deep merging for complex fields like padding and callbacks.
|
||||
func mergeCellConfig(dst, src tw.CellConfig) tw.CellConfig {
|
||||
if src.Formatting.Alignment != tw.Empty {
|
||||
dst.Formatting.Alignment = src.Formatting.Alignment
|
||||
}
|
||||
|
||||
if src.Formatting.AutoWrap != 0 {
|
||||
dst.Formatting.AutoWrap = src.Formatting.AutoWrap
|
||||
}
|
||||
if src.ColMaxWidths.Global != 0 {
|
||||
dst.ColMaxWidths.Global = src.ColMaxWidths.Global
|
||||
}
|
||||
|
||||
// Handle merging of the new CellMerging struct and the deprecated MergeMode
|
||||
if src.Merging.Mode != 0 {
|
||||
dst.Merging.Mode = src.Merging.Mode
|
||||
dst.Formatting.MergeMode = src.Merging.Mode
|
||||
} else if src.Formatting.MergeMode != 0 {
|
||||
dst.Merging.Mode = src.Formatting.MergeMode
|
||||
dst.Formatting.MergeMode = src.Formatting.MergeMode
|
||||
}
|
||||
|
||||
if src.Merging.ByColumnIndex != nil {
|
||||
dst.Merging.ByColumnIndex = src.Merging.ByColumnIndex.Clone()
|
||||
}
|
||||
|
||||
dst.Formatting.AutoFormat = src.Formatting.AutoFormat
|
||||
|
||||
if src.Padding.Global.Paddable() {
|
||||
dst.Padding.Global = src.Padding.Global
|
||||
}
|
||||
|
||||
if len(src.Padding.PerColumn) > 0 {
|
||||
if dst.Padding.PerColumn == nil {
|
||||
dst.Padding.PerColumn = make([]tw.Padding, len(src.Padding.PerColumn))
|
||||
} else if len(src.Padding.PerColumn) > len(dst.Padding.PerColumn) {
|
||||
dst.Padding.PerColumn = append(dst.Padding.PerColumn, make([]tw.Padding, len(src.Padding.PerColumn)-len(dst.Padding.PerColumn))...)
|
||||
}
|
||||
for i, pad := range src.Padding.PerColumn {
|
||||
if pad.Paddable() {
|
||||
dst.Padding.PerColumn[i] = pad
|
||||
}
|
||||
}
|
||||
}
|
||||
if src.Callbacks.Global != nil {
|
||||
dst.Callbacks.Global = src.Callbacks.Global
|
||||
}
|
||||
if len(src.Callbacks.PerColumn) > 0 {
|
||||
if dst.Callbacks.PerColumn == nil {
|
||||
dst.Callbacks.PerColumn = make([]func(), len(src.Callbacks.PerColumn))
|
||||
} else if len(src.Callbacks.PerColumn) > len(dst.Callbacks.PerColumn) {
|
||||
dst.Callbacks.PerColumn = append(dst.Callbacks.PerColumn, make([]func(), len(src.Callbacks.PerColumn)-len(dst.Callbacks.PerColumn))...)
|
||||
}
|
||||
for i, cb := range src.Callbacks.PerColumn {
|
||||
if cb != nil {
|
||||
dst.Callbacks.PerColumn[i] = cb
|
||||
}
|
||||
}
|
||||
}
|
||||
if src.Filter.Global != nil {
|
||||
dst.Filter.Global = src.Filter.Global
|
||||
}
|
||||
if len(src.Filter.PerColumn) > 0 {
|
||||
if dst.Filter.PerColumn == nil {
|
||||
dst.Filter.PerColumn = make([]func(string) string, len(src.Filter.PerColumn))
|
||||
} else if len(src.Filter.PerColumn) > len(dst.Filter.PerColumn) {
|
||||
dst.Filter.PerColumn = append(dst.Filter.PerColumn, make([]func(string) string, len(src.Filter.PerColumn)-len(dst.Filter.PerColumn))...)
|
||||
}
|
||||
for i, filter := range src.Filter.PerColumn {
|
||||
if filter != nil {
|
||||
dst.Filter.PerColumn[i] = filter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge Alignment
|
||||
if src.Alignment.Global != tw.Empty {
|
||||
dst.Alignment.Global = src.Alignment.Global
|
||||
}
|
||||
|
||||
if len(src.Alignment.PerColumn) > 0 {
|
||||
if dst.Alignment.PerColumn == nil {
|
||||
dst.Alignment.PerColumn = make([]tw.Align, len(src.Alignment.PerColumn))
|
||||
} else if len(src.Alignment.PerColumn) > len(dst.Alignment.PerColumn) {
|
||||
dst.Alignment.PerColumn = append(dst.Alignment.PerColumn, make([]tw.Align, len(src.Alignment.PerColumn)-len(dst.Alignment.PerColumn))...)
|
||||
}
|
||||
for i, align := range src.Alignment.PerColumn {
|
||||
if align != tw.Skip {
|
||||
dst.Alignment.PerColumn[i] = align
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(src.ColumnAligns) > 0 {
|
||||
if dst.ColumnAligns == nil {
|
||||
dst.ColumnAligns = make([]tw.Align, len(src.ColumnAligns))
|
||||
} else if len(src.ColumnAligns) > len(dst.ColumnAligns) {
|
||||
dst.ColumnAligns = append(dst.ColumnAligns, make([]tw.Align, len(src.ColumnAligns)-len(dst.ColumnAligns))...)
|
||||
}
|
||||
for i, align := range src.ColumnAligns {
|
||||
if align != tw.Skip {
|
||||
dst.ColumnAligns[i] = align
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(src.ColMaxWidths.PerColumn) > 0 {
|
||||
if dst.ColMaxWidths.PerColumn == nil {
|
||||
dst.ColMaxWidths.PerColumn = make(map[int]int)
|
||||
}
|
||||
for k, v := range src.ColMaxWidths.PerColumn {
|
||||
if v != 0 {
|
||||
dst.ColMaxWidths.PerColumn[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// mergeConfig merges a source Config into a destination Config, prioritizing non-default source values.
|
||||
// It performs deep merging for complex types like Header, Row, Footer, and Stream.
|
||||
func mergeConfig(dst, src Config) Config {
|
||||
if src.MaxWidth != 0 {
|
||||
dst.MaxWidth = src.MaxWidth
|
||||
}
|
||||
|
||||
dst.Debug = src.Debug || dst.Debug
|
||||
dst.Behavior.AutoHide = src.Behavior.AutoHide
|
||||
dst.Behavior.TrimSpace = src.Behavior.TrimSpace
|
||||
dst.Behavior.TrimTab = src.Behavior.TrimTab
|
||||
dst.Behavior.Compact = src.Behavior.Compact
|
||||
dst.Behavior.Header = src.Behavior.Header
|
||||
dst.Behavior.Footer = src.Behavior.Footer
|
||||
dst.Behavior.Footer = src.Behavior.Footer
|
||||
|
||||
dst.Behavior.Structs.AutoHeader = src.Behavior.Structs.AutoHeader
|
||||
|
||||
// check lent of tags
|
||||
if len(src.Behavior.Structs.Tags) > 0 {
|
||||
dst.Behavior.Structs.Tags = src.Behavior.Structs.Tags
|
||||
}
|
||||
|
||||
if src.Widths.Global != 0 {
|
||||
dst.Widths.Global = src.Widths.Global
|
||||
}
|
||||
if len(src.Widths.PerColumn) > 0 {
|
||||
if dst.Widths.PerColumn == nil {
|
||||
dst.Widths.PerColumn = make(map[int]int)
|
||||
}
|
||||
for k, v := range src.Widths.PerColumn {
|
||||
if v != 0 {
|
||||
dst.Widths.PerColumn[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dst.Header = mergeCellConfig(dst.Header, src.Header)
|
||||
dst.Row = mergeCellConfig(dst.Row, src.Row)
|
||||
dst.Footer = mergeCellConfig(dst.Footer, src.Footer)
|
||||
dst.Stream = mergeStreamConfig(dst.Stream, src.Stream)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// mergeStreamConfig merges a source StreamConfig into a destination StreamConfig, prioritizing non-default source values.
|
||||
func mergeStreamConfig(dst, src tw.StreamConfig) tw.StreamConfig {
|
||||
if src.Enable {
|
||||
dst.Enable = true
|
||||
}
|
||||
|
||||
dst.StrictColumns = src.StrictColumns
|
||||
return dst
|
||||
}
|
||||
|
||||
// padLine pads a line to the specified column count by appending empty strings as needed.
|
||||
func padLine(line []string, numCols int) []string {
|
||||
if len(line) >= numCols {
|
||||
return line
|
||||
}
|
||||
padded := make([]string, numCols)
|
||||
copy(padded, line)
|
||||
for i := len(line); i < numCols; i++ {
|
||||
padded[i] = tw.Empty
|
||||
}
|
||||
return padded
|
||||
}
|
||||
Reference in New Issue
Block a user