From a3ed844a8f4412565420cd81ac5ee07549c378b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Wed, 4 Jun 2025 21:18:45 +0200 Subject: [PATCH 1/3] Make package work with updated version v1.0.0 of tablewriter. tablewriter has changed a lot of formatting behavior. This commit tries to emulate the old behavior as much as possible. It may not be complete though. --- html2text.go | 247 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 197 insertions(+), 50 deletions(-) diff --git a/html2text.go b/html2text.go index 8fe9000..60526aa 100644 --- a/html2text.go +++ b/html2text.go @@ -8,6 +8,7 @@ import ( "unicode" "github.com/olekukonko/tablewriter" + "github.com/olekukonko/tablewriter/tw" "github.com/ssor/bom" "golang.org/x/net/html" "golang.org/x/net/html/atom" @@ -21,45 +22,175 @@ type Options struct { TextOnly bool // Returns only plain text } +// Border controls tablewriter borders. It uses simple bools instead of tablewriters `State` +type Border struct { + Left, Right, Bottom, Top bool +} + +func (b Border) withStates() tw.Border { + return tw.Border{ + Left: asState(b.Left), + Right: asState(b.Right), + Bottom: asState(b.Bottom), + Top: asState(b.Top), + } +} + +type BorderStyle struct { + ColumnSeparator string + RowSeparator string + CenterSeparator string +} + +func (b BorderStyle) Name() string { + return "html2text" +} + +func (b BorderStyle) Center() string { + return b.CenterSeparator +} + +func (b BorderStyle) Row() string { + return b.RowSeparator +} + +func (b BorderStyle) Column() string { + return b.ColumnSeparator +} + +func (b BorderStyle) TopLeft() string { + return b.CenterSeparator +} + +func (b BorderStyle) TopMid() string { + return b.CenterSeparator +} + +func (b BorderStyle) TopRight() string { + return b.CenterSeparator +} + +func (b BorderStyle) MidLeft() string { + return b.CenterSeparator +} + +func (b BorderStyle) MidRight() string { + return b.CenterSeparator +} + +func (b BorderStyle) BottomLeft() string { + return b.CenterSeparator +} + +func (b BorderStyle) BottomMid() string { + return b.CenterSeparator +} + +func (b BorderStyle) BottomRight() string { + return b.CenterSeparator +} + +func (b BorderStyle) HeaderLeft() string { + return b.CenterSeparator +} + +func (b BorderStyle) HeaderMid() string { + return b.CenterSeparator +} + +func (b BorderStyle) HeaderRight() string { + return b.CenterSeparator +} + +var defaultBorderStyle = BorderStyle{ + ColumnSeparator: "|", + RowSeparator: "-", + CenterSeparator: "+", +} + // PrettyTablesOptions overrides tablewriter behaviors type PrettyTablesOptions struct { - AutoFormatHeader bool - AutoWrapText bool + AutoFormatHeader bool + AutoWrapText bool + // Deprecated. Tablewriter always assumes this to be `true` ReflowDuringAutoWrap bool ColWidth int ColumnSeparator string RowSeparator string CenterSeparator string - HeaderAlignment int - FooterAlignment int - Alignment int - ColumnAlignment []int - NewLine string - HeaderLine bool - RowLine bool - AutoMergeCells bool - Borders tablewriter.Border + HeaderAlignment tw.Align + FooterAlignment tw.Align + Alignment tw.Align + ColumnAlignment tw.Alignment + // Deprecated. Tablewriter always assumes this to be `\n` + NewLine string + HeaderLine bool + RowLine bool + AutoMergeCells bool + Borders Border +} + +func (p *PrettyTablesOptions) wrapMode() int { + if p.AutoWrapText { + return tw.WrapNormal + } else { + return tw.WrapNone + } +} + +func (p *PrettyTablesOptions) borderStyle() BorderStyle { + return BorderStyle{ + ColumnSeparator: p.ColumnSeparator, + RowSeparator: p.RowSeparator, + CenterSeparator: p.CenterSeparator, + } +} + +func (p *PrettyTablesOptions) renderSettings() tw.Settings { + return tw.Settings{ + Lines: tw.Lines{ + ShowHeaderLine: asState(p.HeaderLine), + }, + Separators: tw.Separators{ + BetweenRows: asState(p.RowLine), + }, + } +} + +func (p *PrettyTablesOptions) mergeMode() int { + if p.AutoMergeCells { + return tw.MergeVertical + } else { + return tw.MergeNone + } +} + +func asState(b bool) tw.State { + // TableWriter does not provide this by default :( + if b { + return tw.On + } else { + return tw.Off + } } // NewPrettyTablesOptions creates PrettyTablesOptions with default settings func NewPrettyTablesOptions() *PrettyTablesOptions { return &PrettyTablesOptions{ - AutoFormatHeader: true, - AutoWrapText: true, - ReflowDuringAutoWrap: true, - ColWidth: tablewriter.MAX_ROW_WIDTH, - ColumnSeparator: tablewriter.COLUMN, - RowSeparator: tablewriter.ROW, - CenterSeparator: tablewriter.CENTER, - HeaderAlignment: tablewriter.ALIGN_DEFAULT, - FooterAlignment: tablewriter.ALIGN_DEFAULT, - Alignment: tablewriter.ALIGN_DEFAULT, - ColumnAlignment: []int{}, - NewLine: tablewriter.NEWLINE, - HeaderLine: true, - RowLine: false, - AutoMergeCells: false, - Borders: tablewriter.Border{Left: true, Right: true, Bottom: true, Top: true}, + AutoFormatHeader: true, + AutoWrapText: true, + ColWidth: 32, // old tablewriter.MAX_ROW_WIDTH + borders now count into width + ColumnSeparator: defaultBorderStyle.ColumnSeparator, + RowSeparator: defaultBorderStyle.RowSeparator, + CenterSeparator: defaultBorderStyle.CenterSeparator, + HeaderAlignment: tw.AlignCenter, + FooterAlignment: tw.AlignCenter, + Alignment: tw.AlignDefault, + ColumnAlignment: make(tw.Alignment, 0), + HeaderLine: true, + RowLine: false, + AutoMergeCells: false, + Borders: Border{Left: true, Right: true, Bottom: true, Top: true}, } } @@ -70,6 +201,11 @@ func FromHTMLNode(doc *html.Node, o ...Options) (string, error) { options = o[0] } + if options.PrettyTables && options.PrettyTablesOptions == nil { + // defaults need to make explicit as they are no longer identical with tablewriter + options.PrettyTablesOptions = NewPrettyTablesOptions() + } + ctx := textifyTraverseContext{ buf: bytes.Buffer{}, options: options, @@ -333,31 +469,42 @@ func (ctx *textifyTraverseContext) handleTableElement(node *html.Node) error { buf := &bytes.Buffer{} table := tablewriter.NewWriter(buf) - if ctx.options.PrettyTablesOptions != nil { - options := ctx.options.PrettyTablesOptions - table.SetAutoFormatHeaders(options.AutoFormatHeader) - table.SetAutoWrapText(options.AutoWrapText) - table.SetReflowDuringAutoWrap(options.ReflowDuringAutoWrap) - table.SetColWidth(options.ColWidth) - table.SetColumnSeparator(options.ColumnSeparator) - table.SetRowSeparator(options.RowSeparator) - table.SetCenterSeparator(options.CenterSeparator) - table.SetHeaderAlignment(options.HeaderAlignment) - table.SetFooterAlignment(options.FooterAlignment) - table.SetAlignment(options.Alignment) - table.SetColumnAlignment(options.ColumnAlignment) - table.SetNewLine(options.NewLine) - table.SetHeaderLine(options.HeaderLine) - table.SetRowLine(options.RowLine) - table.SetAutoMergeCells(options.AutoMergeCells) - table.SetBorders(options.Borders) - } - table.SetHeader(ctx.tableCtx.header) - table.SetFooter(ctx.tableCtx.footer) - table.AppendBulk(ctx.tableCtx.body) + + options := ctx.options.PrettyTablesOptions + cfg := tablewriter.NewConfigBuilder() + + cfg.WithHeaderAutoFormat(asState(options.AutoFormatHeader)).WithFooterAutoFormat(asState(options.AutoFormatHeader)). + WithRowAutoWrap(options.wrapMode()).WithHeaderAutoWrap(options.wrapMode()).WithFooterAutoWrap(options.wrapMode()). + WithRowMaxWidth(options.ColWidth). + WithHeaderAlignment(options.HeaderAlignment). + WithFooterAlignment(options.FooterAlignment). + WithRowAlignment(options.Alignment). + WithRowMergeMode(options.mergeMode()) + + if len(options.ColumnAlignment) > 0 { + cfg.Row().Alignment().WithPerColumn(options.ColumnAlignment) + } + + rendition := tw.Rendition{ + Borders: options.Borders.withStates(), + Symbols: options.borderStyle(), + Settings: options.renderSettings(), + } + + table.Options( + tablewriter.WithConfig(cfg.Build()), + tablewriter.WithRendition(rendition)) + + table.Header(ctx.tableCtx.header) + table.Footer(ctx.tableCtx.footer) + if err := table.Bulk(ctx.tableCtx.body); err != nil { + return err + } // Render the table using ASCII. - table.Render() + if err := table.Render(); err != nil { + return err + } if err := ctx.emit(buf.String()); err != nil { return err } From 7c3ced725b7cf4ad0a543188a4c2396c81521c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Wed, 4 Jun 2025 21:30:24 +0200 Subject: [PATCH 2/3] Move handling of all the pretty-table stuff into its own file. This declutters html2text.go --- html2text.go | 199 +--------------------------------------------- prettytables.go | 204 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+), 198 deletions(-) create mode 100644 prettytables.go diff --git a/html2text.go b/html2text.go index 60526aa..05aeeb3 100644 --- a/html2text.go +++ b/html2text.go @@ -8,7 +8,6 @@ import ( "unicode" "github.com/olekukonko/tablewriter" - "github.com/olekukonko/tablewriter/tw" "github.com/ssor/bom" "golang.org/x/net/html" "golang.org/x/net/html/atom" @@ -22,178 +21,6 @@ type Options struct { TextOnly bool // Returns only plain text } -// Border controls tablewriter borders. It uses simple bools instead of tablewriters `State` -type Border struct { - Left, Right, Bottom, Top bool -} - -func (b Border) withStates() tw.Border { - return tw.Border{ - Left: asState(b.Left), - Right: asState(b.Right), - Bottom: asState(b.Bottom), - Top: asState(b.Top), - } -} - -type BorderStyle struct { - ColumnSeparator string - RowSeparator string - CenterSeparator string -} - -func (b BorderStyle) Name() string { - return "html2text" -} - -func (b BorderStyle) Center() string { - return b.CenterSeparator -} - -func (b BorderStyle) Row() string { - return b.RowSeparator -} - -func (b BorderStyle) Column() string { - return b.ColumnSeparator -} - -func (b BorderStyle) TopLeft() string { - return b.CenterSeparator -} - -func (b BorderStyle) TopMid() string { - return b.CenterSeparator -} - -func (b BorderStyle) TopRight() string { - return b.CenterSeparator -} - -func (b BorderStyle) MidLeft() string { - return b.CenterSeparator -} - -func (b BorderStyle) MidRight() string { - return b.CenterSeparator -} - -func (b BorderStyle) BottomLeft() string { - return b.CenterSeparator -} - -func (b BorderStyle) BottomMid() string { - return b.CenterSeparator -} - -func (b BorderStyle) BottomRight() string { - return b.CenterSeparator -} - -func (b BorderStyle) HeaderLeft() string { - return b.CenterSeparator -} - -func (b BorderStyle) HeaderMid() string { - return b.CenterSeparator -} - -func (b BorderStyle) HeaderRight() string { - return b.CenterSeparator -} - -var defaultBorderStyle = BorderStyle{ - ColumnSeparator: "|", - RowSeparator: "-", - CenterSeparator: "+", -} - -// PrettyTablesOptions overrides tablewriter behaviors -type PrettyTablesOptions struct { - AutoFormatHeader bool - AutoWrapText bool - // Deprecated. Tablewriter always assumes this to be `true` - ReflowDuringAutoWrap bool - ColWidth int - ColumnSeparator string - RowSeparator string - CenterSeparator string - HeaderAlignment tw.Align - FooterAlignment tw.Align - Alignment tw.Align - ColumnAlignment tw.Alignment - // Deprecated. Tablewriter always assumes this to be `\n` - NewLine string - HeaderLine bool - RowLine bool - AutoMergeCells bool - Borders Border -} - -func (p *PrettyTablesOptions) wrapMode() int { - if p.AutoWrapText { - return tw.WrapNormal - } else { - return tw.WrapNone - } -} - -func (p *PrettyTablesOptions) borderStyle() BorderStyle { - return BorderStyle{ - ColumnSeparator: p.ColumnSeparator, - RowSeparator: p.RowSeparator, - CenterSeparator: p.CenterSeparator, - } -} - -func (p *PrettyTablesOptions) renderSettings() tw.Settings { - return tw.Settings{ - Lines: tw.Lines{ - ShowHeaderLine: asState(p.HeaderLine), - }, - Separators: tw.Separators{ - BetweenRows: asState(p.RowLine), - }, - } -} - -func (p *PrettyTablesOptions) mergeMode() int { - if p.AutoMergeCells { - return tw.MergeVertical - } else { - return tw.MergeNone - } -} - -func asState(b bool) tw.State { - // TableWriter does not provide this by default :( - if b { - return tw.On - } else { - return tw.Off - } -} - -// NewPrettyTablesOptions creates PrettyTablesOptions with default settings -func NewPrettyTablesOptions() *PrettyTablesOptions { - return &PrettyTablesOptions{ - AutoFormatHeader: true, - AutoWrapText: true, - ColWidth: 32, // old tablewriter.MAX_ROW_WIDTH + borders now count into width - ColumnSeparator: defaultBorderStyle.ColumnSeparator, - RowSeparator: defaultBorderStyle.RowSeparator, - CenterSeparator: defaultBorderStyle.CenterSeparator, - HeaderAlignment: tw.AlignCenter, - FooterAlignment: tw.AlignCenter, - Alignment: tw.AlignDefault, - ColumnAlignment: make(tw.Alignment, 0), - HeaderLine: true, - RowLine: false, - AutoMergeCells: false, - Borders: Border{Left: true, Right: true, Bottom: true, Top: true}, - } -} - // FromHTMLNode renders text output from a pre-parsed HTML document. func FromHTMLNode(doc *html.Node, o ...Options) (string, error) { var options Options @@ -469,31 +296,7 @@ func (ctx *textifyTraverseContext) handleTableElement(node *html.Node) error { buf := &bytes.Buffer{} table := tablewriter.NewWriter(buf) - - options := ctx.options.PrettyTablesOptions - cfg := tablewriter.NewConfigBuilder() - - cfg.WithHeaderAutoFormat(asState(options.AutoFormatHeader)).WithFooterAutoFormat(asState(options.AutoFormatHeader)). - WithRowAutoWrap(options.wrapMode()).WithHeaderAutoWrap(options.wrapMode()).WithFooterAutoWrap(options.wrapMode()). - WithRowMaxWidth(options.ColWidth). - WithHeaderAlignment(options.HeaderAlignment). - WithFooterAlignment(options.FooterAlignment). - WithRowAlignment(options.Alignment). - WithRowMergeMode(options.mergeMode()) - - if len(options.ColumnAlignment) > 0 { - cfg.Row().Alignment().WithPerColumn(options.ColumnAlignment) - } - - rendition := tw.Rendition{ - Borders: options.Borders.withStates(), - Symbols: options.borderStyle(), - Settings: options.renderSettings(), - } - - table.Options( - tablewriter.WithConfig(cfg.Build()), - tablewriter.WithRendition(rendition)) + ctx.options.PrettyTablesOptions.configureTable(table) table.Header(ctx.tableCtx.header) table.Footer(ctx.tableCtx.footer) diff --git a/prettytables.go b/prettytables.go new file mode 100644 index 0000000..faca3fa --- /dev/null +++ b/prettytables.go @@ -0,0 +1,204 @@ +package html2text + +import ( + "github.com/olekukonko/tablewriter" + "github.com/olekukonko/tablewriter/tw" +) + +// PrettyTablesOptions overrides tablewriter behaviors +type PrettyTablesOptions struct { + AutoFormatHeader bool + AutoWrapText bool + // Deprecated. Tablewriter always assumes this to be `true` + ReflowDuringAutoWrap bool + ColWidth int + ColumnSeparator string + RowSeparator string + CenterSeparator string + HeaderAlignment tw.Align + FooterAlignment tw.Align + Alignment tw.Align + ColumnAlignment tw.Alignment + // Deprecated. Tablewriter always assumes this to be `\n` + NewLine string + HeaderLine bool + RowLine bool + AutoMergeCells bool + Borders Border +} + +// NewPrettyTablesOptions creates PrettyTablesOptions with default settings +func NewPrettyTablesOptions() *PrettyTablesOptions { + return &PrettyTablesOptions{ + AutoFormatHeader: true, + AutoWrapText: true, + ColWidth: 32, // old tablewriter.MAX_ROW_WIDTH + borders now count into width + ColumnSeparator: defaultBorderStyle.ColumnSeparator, + RowSeparator: defaultBorderStyle.RowSeparator, + CenterSeparator: defaultBorderStyle.CenterSeparator, + HeaderAlignment: tw.AlignCenter, + FooterAlignment: tw.AlignCenter, + Alignment: tw.AlignDefault, + ColumnAlignment: make(tw.Alignment, 0), + HeaderLine: true, + RowLine: false, + AutoMergeCells: false, + Borders: Border{Left: true, Right: true, Bottom: true, Top: true}, + } +} + +func (p *PrettyTablesOptions) configureTable(table *tablewriter.Table) { + cfg := tablewriter.NewConfigBuilder() + + cfg.WithHeaderAutoFormat(asState(p.AutoFormatHeader)).WithFooterAutoFormat(asState(p.AutoFormatHeader)). + WithRowAutoWrap(p.wrapMode()).WithHeaderAutoWrap(p.wrapMode()).WithFooterAutoWrap(p.wrapMode()). + WithRowMaxWidth(p.ColWidth). + WithHeaderAlignment(p.HeaderAlignment). + WithFooterAlignment(p.FooterAlignment). + WithRowAlignment(p.Alignment). + WithRowMergeMode(p.mergeMode()) + + if len(p.ColumnAlignment) > 0 { + cfg.Row().Alignment().WithPerColumn(p.ColumnAlignment) + } + + rendition := tw.Rendition{ + Borders: p.Borders.withStates(), + Symbols: p.borderStyle(), + Settings: p.renderSettings(), + } + + table.Options( + tablewriter.WithConfig(cfg.Build()), + tablewriter.WithRendition(rendition)) +} + +func (p *PrettyTablesOptions) wrapMode() int { + if p.AutoWrapText { + return tw.WrapNormal + } else { + return tw.WrapNone + } +} + +func (p *PrettyTablesOptions) mergeMode() int { + if p.AutoMergeCells { + return tw.MergeVertical + } else { + return tw.MergeNone + } +} + +func (p *PrettyTablesOptions) renderSettings() tw.Settings { + return tw.Settings{ + Lines: tw.Lines{ + ShowHeaderLine: asState(p.HeaderLine), + }, + Separators: tw.Separators{ + BetweenRows: asState(p.RowLine), + }, + } +} + +// Border controls tablewriter borders. It uses simple bools instead of tablewriters `State` +type Border struct { + Left, Right, Bottom, Top bool +} + +func (b Border) withStates() tw.Border { + return tw.Border{ + Left: asState(b.Left), + Right: asState(b.Right), + Bottom: asState(b.Bottom), + Top: asState(b.Top), + } +} + +type BorderStyle struct { + ColumnSeparator string + RowSeparator string + CenterSeparator string +} + +func (b BorderStyle) Name() string { + return "html2text" +} + +func (b BorderStyle) Center() string { + return b.CenterSeparator +} + +func (b BorderStyle) Row() string { + return b.RowSeparator +} + +func (b BorderStyle) Column() string { + return b.ColumnSeparator +} + +func (b BorderStyle) TopLeft() string { + return b.CenterSeparator +} + +func (b BorderStyle) TopMid() string { + return b.CenterSeparator +} + +func (b BorderStyle) TopRight() string { + return b.CenterSeparator +} + +func (b BorderStyle) MidLeft() string { + return b.CenterSeparator +} + +func (b BorderStyle) MidRight() string { + return b.CenterSeparator +} + +func (b BorderStyle) BottomLeft() string { + return b.CenterSeparator +} + +func (b BorderStyle) BottomMid() string { + return b.CenterSeparator +} + +func (b BorderStyle) BottomRight() string { + return b.CenterSeparator +} + +func (b BorderStyle) HeaderLeft() string { + return b.CenterSeparator +} + +func (b BorderStyle) HeaderMid() string { + return b.CenterSeparator +} + +func (b BorderStyle) HeaderRight() string { + return b.CenterSeparator +} + +var defaultBorderStyle = BorderStyle{ + ColumnSeparator: "|", + RowSeparator: "-", + CenterSeparator: "+", +} + +func (p *PrettyTablesOptions) borderStyle() BorderStyle { + return BorderStyle{ + ColumnSeparator: p.ColumnSeparator, + RowSeparator: p.RowSeparator, + CenterSeparator: p.CenterSeparator, + } +} + +func asState(b bool) tw.State { + // TableWriter does not provide this by default :( + if b { + return tw.On + } else { + return tw.Off + } +} From 7f7f295e1c5e97ac493309ef7ef0d339061b6b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Wed, 4 Jun 2025 21:55:14 +0200 Subject: [PATCH 3/3] Add the possibility to bypass all the legacy tablewriter handling and do all the configuration directly. The place for the configuration function is slightly odd, but avoids larger refactoring and possible breaking changes for consumers. --- html2text_test.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++ prettytables.go | 8 ++++++++ 2 files changed, 59 insertions(+) diff --git a/html2text_test.go b/html2text_test.go index 452b45e..cb418a2 100644 --- a/html2text_test.go +++ b/html2text_test.go @@ -9,6 +9,9 @@ import ( "regexp" "strings" "testing" + + "github.com/olekukonko/tablewriter" + "github.com/olekukonko/tablewriter/tw" ) const destPath = "testdata" @@ -353,6 +356,54 @@ Table 2 Header 1 Table 2 Header 2 Table 2 Footer 1 Table 2 Footer 2 Table 2 Row } } +func TestTablesWithDirectConfiguration(t *testing.T) { + testCases := []struct { + input string + tabularOutput string + }{ + { + ` + + + + + + + + + + +
Header 1Header 2
Footer 1Footer 2
Row 1 Col 1Row 1 Col 2
Row 2 Col 1Row 2 Col 2
`, + `┌─────────────┬─────────────┐ +│ HEADER 1 │ HEADER 2 │ +├─────────────┼─────────────┤ +│ Row 1 Col 1 │ Row 1 Col 2 │ +│ Row 2 Col 1 │ Row 2 Col 2 │ +└─────────────┴─────────────┘`, + }, + } + + for _, testCase := range testCases { + options := Options{ + PrettyTables: true, + PrettyTablesOptions: &PrettyTablesOptions{ + Configuration: func(table *tablewriter.Table) { + table.Options( + tablewriter.WithHeaderAlignment(tw.AlignRight), + tablewriter.WithFooterControl(tw.Control{Hide: tw.On}), + ) + }, + }, + } + // Check pretty tabular ASCII version. + if msg, err := wantString(testCase.input, testCase.tabularOutput, options); err != nil { + t.Error(err) + } else if len(msg) > 0 { + t.Log(msg) + } + } +} + func TestStrippingLists(t *testing.T) { testCases := []struct { input string diff --git a/prettytables.go b/prettytables.go index faca3fa..273483d 100644 --- a/prettytables.go +++ b/prettytables.go @@ -25,6 +25,9 @@ type PrettyTablesOptions struct { RowLine bool AutoMergeCells bool Borders Border + // Configuration allows to directly manipulate the `Table` with all what [tablewriter] offers. + // Setting this ignores all the rest of the settings of this struct. + Configuration func(table *tablewriter.Table) } // NewPrettyTablesOptions creates PrettyTablesOptions with default settings @@ -48,6 +51,11 @@ func NewPrettyTablesOptions() *PrettyTablesOptions { } func (p *PrettyTablesOptions) configureTable(table *tablewriter.Table) { + if p.Configuration != nil { + p.Configuration(table) + return + } + cfg := tablewriter.NewConfigBuilder() cfg.WithHeaderAutoFormat(asState(p.AutoFormatHeader)).WithFooterAutoFormat(asState(p.AutoFormatHeader)).