.Alignment()`) (`config.go`). |
+| ANSI code artifacts | **Cause**: Non-ANSI terminal. **Solution**: Provide non-colored fallback (`tw/fn.go`). |
+| Slow rendering | **Cause**: Complex filters or merging. **Solution**: Optimize logic; limit merging (`zoo.go`). |
+| Uneven cell counts | **Cause**: Mismatched rows/headers. **Solution**: Pad with `""` (`zoo.go`). |
+| Border inconsistencies | **Cause**: Mismatched `Borders`/`Symbols`. **Solution**: Align settings (`tw/renderer.go`). |
+| Streaming width issues | **Cause**: No `Config.Widths`. **Solution**: Set before `Start()` (`stream.go`). |
+| Formatter ignored | **Cause**: `WithStringer` might take precedence if compatible. **Solution**: Review conversion priority; `tw.Formatter` is high-priority for single-item-to-single-cell conversion (`zoo.go`). |
+| Caption misalignment | **Cause**: Incorrect `Width` or `Align`. **Solution**: Set `tw.Caption.Width`/`Align` (`tablewriter.go`). |
+| Per-column padding errors | **Cause**: Incorrect indexing in `Padding.PerColumn`. **Solution**: Verify indices (`tw/cell.go`). |
+| Vertical merging in streaming | **Cause**: Unsupported. **Solution**: Use batch mode (`stream.go`). |
+| Filter performance | **Cause**: Complex logic. **Solution**: Simplify filters (`zoo.go`). |
+| Custom symbols incomplete | **Cause**: Missing characters. **Solution**: Define all symbols (`tw/symbols.go`). |
+| Table too wide | **Cause**: No `MaxWidth`. **Solution**: Set `Config.MaxWidth` (`config.go`). |
+| Streaming errors | **Cause**: Missing `Start()`. **Solution**: Call `Start()` before data input (`stream.go`). |
+
+## Additional Notes
+
+- **Performance Optimization**: Enable `WithStringerCache` for repetitive data types when using `WithStringer`; optimize filters and merging for large datasets (`tablewriter.go`, `zoo.go`).
+- **Debugging**: Use `WithDebug(true)` and `table.Debug()` to log configuration and rendering details; invaluable for troubleshooting (`config.go`).
+- **Testing Resources**: The `tests/` directory contains examples of various configurations.
+- **Community Support**: For advanced use cases or issues, consult the source code or open an issue on the `tablewriter` repository.
+- **Future Considerations**: Deprecated methods in `deprecated.go` (e.g., `WithBorders`) are slated for removal in future releases; migrate promptly to ensure compatibility.
+
+This guide aims to cover all migration scenarios comprehensively. For highly specific or advanced use cases, refer to the source files (`config.go`, `tablewriter.go`, `stream.go`, `tw/*`) or engage with the `tablewriter` community for support.
\ No newline at end of file
diff --git a/vendor/github.com/olekukonko/tablewriter/README.md b/vendor/github.com/olekukonko/tablewriter/README.md
index 8aecd0f55b..f694340f73 100644
--- a/vendor/github.com/olekukonko/tablewriter/README.md
+++ b/vendor/github.com/olekukonko/tablewriter/README.md
@@ -28,14 +28,14 @@ go get github.com/olekukonko/tablewriter@v0.0.5
#### Latest Version
The latest stable version
```bash
-go get github.com/olekukonko/tablewriter@v1.0.6
+go get github.com/olekukonko/tablewriter@v1.0.7
```
**Warning:** Version `v1.0.0` contains missing functionality and should not be used.
> **Version Guidance**
-> - Production: Use `v0.0.5` (stable)
+> - Legacy: Use `v0.0.5` (stable)
> - New Features: Use `@latest` (includes generics, super fast streaming APIs)
> - Legacy Docs: See [README_LEGACY.md](README_LEGACY.md)
@@ -62,7 +62,7 @@ func main() {
data := [][]string{
{"Package", "Version", "Status"},
{"tablewriter", "v0.0.5", "legacy"},
- {"tablewriter", "v1.0.6", "latest"},
+ {"tablewriter", "v1.0.7", "latest"},
}
table := tablewriter.NewWriter(os.Stdout)
@@ -77,7 +77,7 @@ func main() {
│ PACKAGE │ VERSION │ STATUS │
├─────────────┼─────────┼────────┤
│ tablewriter │ v0.0.5 │ legacy │
-│ tablewriter │ v1.0.6 │ latest │
+│ tablewriter │ v1.0.7 │ latest │
└─────────────┴─────────┴────────┘
```
@@ -297,7 +297,7 @@ func main() {
}
table.Configure(func(config *tablewriter.Config) {
- config.Row.Formatting.Alignment = tw.AlignLeft
+ config.Row.Alignment.Global = tw.AlignLeft
})
table.Render()
}
@@ -368,14 +368,12 @@ func main() {
tablewriter.WithRenderer(renderer.NewColorized(colorCfg)),
tablewriter.WithConfig(tablewriter.Config{
Row: tw.CellConfig{
- Formatting: tw.CellFormatting{
- AutoWrap: tw.WrapNormal, // Wrap long content
- Alignment: tw.AlignLeft, // Left-align rows
- },
+ Formatting: tw.CellFormatting{AutoWrap: tw.WrapNormal}, // Wrap long content
+ Alignment: tw.CellAlignment{Global: tw.AlignLeft}, // Left-align rows
ColMaxWidths: tw.CellWidth{Global: 25},
},
Footer: tw.CellConfig{
- Formatting: tw.CellFormatting{Alignment: tw.AlignRight},
+ Alignment: tw.CellAlignment{Global: tw.AlignRight},
},
}),
)
@@ -480,19 +478,13 @@ func main() {
table := tablewriter.NewTable(os.Stdout,
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{
- Settings: tw.Settings{
- Separators: tw.Separators{BetweenRows: tw.On},
- },
+ Settings: tw.Settings{Separators: tw.Separators{BetweenRows: tw.On}},
})),
tablewriter.WithConfig(tablewriter.Config{
- Header: tw.CellConfig{
- Formatting: tw.CellFormatting{Alignment: tw.AlignCenter},
- },
+ Header: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignCenter}},
Row: tw.CellConfig{
- Formatting: tw.CellFormatting{
- MergeMode: tw.MergeHierarchical,
- Alignment: tw.AlignLeft,
- },
+ Formatting: tw.CellFormatting{MergeMode: tw.MergeHierarchical},
+ Alignment: tw.CellAlignment{Global: tw.AlignLeft},
},
}),
)
@@ -546,21 +538,20 @@ func main() {
table := tablewriter.NewTable(os.Stdout,
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{
- Settings: tw.Settings{
- Separators: tw.Separators{BetweenRows: tw.On},
- },
+ Settings: tw.Settings{Separators: tw.Separators{BetweenRows: tw.On}},
})),
tablewriter.WithConfig(tablewriter.Config{
Row: tw.CellConfig{
- Formatting: tw.CellFormatting{MergeMode: tw.MergeBoth},
- ColumnAligns: []tw.Align{tw.Skip, tw.Skip, tw.AlignRight, tw.AlignLeft},
+ Formatting: tw.CellFormatting{MergeMode: tw.MergeBoth},
+ Alignment: tw.CellAlignment{PerColumn: []tw.Align{tw.Skip, tw.Skip, tw.AlignRight, tw.AlignLeft}},
},
+
Footer: tw.CellConfig{
Padding: tw.CellPadding{
Global: tw.Padding{Left: "*", Right: "*"},
PerColumn: []tw.Padding{{}, {}, {Bottom: "^"}, {Bottom: "^"}},
},
- ColumnAligns: []tw.Align{tw.Skip, tw.Skip, tw.AlignRight, tw.AlignLeft},
+ Alignment: tw.CellAlignment{PerColumn: []tw.Align{tw.Skip, tw.Skip, tw.AlignRight, tw.AlignLeft}},
},
}),
)
@@ -617,9 +608,7 @@ func main() {
})),
tablewriter.WithConfig(tablewriter.Config{
MaxWidth: 10,
- Row: tw.CellConfig{
- Formatting: tw.CellFormatting{Alignment: tw.AlignCenter},
- },
+ Row: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignCenter}},
}),
)
table.Append([]string{s, s})
@@ -631,16 +620,12 @@ func main() {
// Main table
table := tablewriter.NewTable(os.Stdout,
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{
- Borders: tw.BorderNone,
- Settings: tw.Settings{
- Separators: tw.Separators{BetweenColumns: tw.On},
- },
+ Borders: tw.BorderNone,
+ Settings: tw.Settings{Separators: tw.Separators{BetweenColumns: tw.On}},
})),
tablewriter.WithConfig(tablewriter.Config{
MaxWidth: 30,
- Row: tw.CellConfig{
- Formatting: tw.CellFormatting{Alignment: tw.AlignCenter},
- },
+ Row: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignCenter}},
}),
)
table.Append([]string{createSubTable("A"), createSubTable("B")})
@@ -711,14 +696,11 @@ func main() {
tablewriter.WithStringer(employeeStringer),
tablewriter.WithConfig(tablewriter.Config{
Header: tw.CellConfig{
- Formatting: tw.CellFormatting{Alignment: tw.AlignCenter, AutoFormat: tw.On},
- },
- Row: tw.CellConfig{
- Formatting: tw.CellFormatting{Alignment: tw.AlignLeft},
- },
- Footer: tw.CellConfig{
- Formatting: tw.CellFormatting{Alignment: tw.AlignRight},
+ Formatting: tw.CellFormatting{AutoFormat: tw.On},
+ Alignment: tw.CellAlignment{Global: tw.AlignCenter},
},
+ Row: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignLeft}},
+ Footer: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignRight}},
}),
)
table.Header([]string{"ID", "Name", "Age", "Department", "Salary"})
@@ -784,23 +766,17 @@ func main() {
}
table := tablewriter.NewTable(os.Stdout,
- tablewriter.WithRenderer(renderer.NewHTML(os.Stdout, false, htmlCfg)),
+ tablewriter.WithRenderer(renderer.NewHTML(htmlCfg)),
tablewriter.WithConfig(tablewriter.Config{
Header: tw.CellConfig{
- Formatting: tw.CellFormatting{
- Alignment: tw.AlignCenter,
- MergeMode: tw.MergeHorizontal, // Merge identical header cells
- },
+ Formatting: tw.CellFormatting{MergeMode: tw.MergeHorizontal}, // Merge identical header cells
+ Alignment: tw.CellAlignment{Global: tw.AlignCenter},
},
Row: tw.CellConfig{
- Formatting: tw.CellFormatting{
- MergeMode: tw.MergeHorizontal, // Merge identical row cells
- Alignment: tw.AlignLeft,
- },
- },
- Footer: tw.CellConfig{
- Formatting: tw.CellFormatting{Alignment: tw.AlignRight},
+ Formatting: tw.CellFormatting{MergeMode: tw.MergeHorizontal}, // Merge identical row cells
+ Alignment: tw.CellAlignment{Global: tw.AlignLeft},
},
+ Footer: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignRight}},
}),
)
diff --git a/vendor/github.com/olekukonko/tablewriter/config.go b/vendor/github.com/olekukonko/tablewriter/config.go
index 64c55786c7..94094f1b00 100644
--- a/vendor/github.com/olekukonko/tablewriter/config.go
+++ b/vendor/github.com/olekukonko/tablewriter/config.go
@@ -1,89 +1,71 @@
-// Package tablewriter provides functionality for creating and formatting tables with customizable configurations.
package tablewriter
import (
- "github.com/olekukonko/ll" // Logging library for debug output
- "github.com/olekukonko/tablewriter/tw" // Table writer core types and utilities
- "io" // Input/output interfaces
- "reflect" // Reflection for type handling
+ "github.com/olekukonko/tablewriter/tw"
)
-// ColumnConfigBuilder is used to configure settings for a specific column across all table sections (header, row, footer).
-type ColumnConfigBuilder struct {
- parent *ConfigBuilder // Reference to the parent ConfigBuilder for chaining
- col int // Index of the column being configured
-}
-
-// Build returns the parent ConfigBuilder to allow method chaining.
-func (c *ColumnConfigBuilder) Build() *ConfigBuilder {
- return c.parent
+// Config represents the table configuration
+type Config struct {
+ MaxWidth int
+ Header tw.CellConfig
+ Row tw.CellConfig
+ Footer tw.CellConfig
+ Debug bool
+ Stream tw.StreamConfig
+ Behavior tw.Behavior
+ Widths tw.CellWidth
}
-// WithAlignment sets the text alignment for a specific column in the header section only.
-// Invalid alignments are ignored, and the method returns the builder for chaining.
-func (c *ColumnConfigBuilder) WithAlignment(align tw.Align) *ColumnConfigBuilder {
- if align != tw.AlignLeft && align != tw.AlignRight && align != tw.AlignCenter && align != tw.AlignNone {
- return c
- }
- // Ensure the ColumnAligns slice is large enough to accommodate the column index
- if len(c.parent.config.Header.ColumnAligns) <= c.col {
- newAligns := make([]tw.Align, c.col+1)
- copy(newAligns, c.parent.config.Header.ColumnAligns)
- c.parent.config.Header.ColumnAligns = newAligns
- }
- c.parent.config.Header.ColumnAligns[c.col] = align
- return c
+// ConfigBuilder provides a fluent interface for building Config
+type ConfigBuilder struct {
+ config Config
}
-// WithMaxWidth sets the maximum width for a specific column across all sections (header, row, footer).
-// Negative widths are ignored, and the method returns the builder for chaining.
-func (c *ColumnConfigBuilder) WithMaxWidth(width int) *ColumnConfigBuilder {
- if width < 0 {
- return c
- }
- // Initialize PerColumn maps if they don't exist
- if c.parent.config.Header.ColMaxWidths.PerColumn == nil {
- c.parent.config.Header.ColMaxWidths.PerColumn = make(map[int]int)
- c.parent.config.Row.ColMaxWidths.PerColumn = make(map[int]int)
- c.parent.config.Footer.ColMaxWidths.PerColumn = make(map[int]int)
+// NewConfigBuilder creates a new ConfigBuilder with defaults
+func NewConfigBuilder() *ConfigBuilder {
+ return &ConfigBuilder{
+ config: defaultConfig(),
}
- c.parent.config.Header.ColMaxWidths.PerColumn[c.col] = width
- c.parent.config.Row.ColMaxWidths.PerColumn[c.col] = width
- c.parent.config.Footer.ColMaxWidths.PerColumn[c.col] = width
- return c
}
-// Config represents the overall configuration for a table, including settings for header, rows, footer, and behavior.
-type Config struct {
- MaxWidth int // Maximum width of the entire table (0 for unlimited)
- Header tw.CellConfig // Configuration for the header section
- Row tw.CellConfig // Configuration for the row section
- Footer tw.CellConfig // Configuration for the footer section
- Debug bool // Enables debug logging when true
- Stream tw.StreamConfig // Configuration specific to streaming mode
- Behavior Behavior // Behavioral settings like auto-hiding and trimming
+// Build returns the built Config
+func (b *ConfigBuilder) Build() Config {
+ return b.config
}
-// ConfigBuilder provides a fluent interface for building a Config struct with both direct and nested configuration methods.
-type ConfigBuilder struct {
- config Config // The configuration being built
+// Header returns a HeaderConfigBuilder for header configuration
+func (b *ConfigBuilder) Header() *HeaderConfigBuilder {
+ return &HeaderConfigBuilder{
+ parent: b,
+ config: &b.config.Header,
+ }
}
-// Build finalizes and returns the Config struct after all modifications.
-func (b *ConfigBuilder) Build() Config {
- return b.config
+// Row returns a RowConfigBuilder for row configuration
+func (b *ConfigBuilder) Row() *RowConfigBuilder {
+ return &RowConfigBuilder{
+ parent: b,
+ config: &b.config.Row,
+ }
}
-// Footer returns a builder for advanced configuration of the footer section.
+// Footer returns a FooterConfigBuilder for footer configuration
func (b *ConfigBuilder) Footer() *FooterConfigBuilder {
return &FooterConfigBuilder{
- parent: b,
- config: &b.config.Footer,
- section: "footer",
+ parent: b,
+ config: &b.config.Footer,
+ }
+}
+
+// Behavior returns a BehaviorConfigBuilder for behavior configuration
+func (b *ConfigBuilder) Behavior() *BehaviorConfigBuilder {
+ return &BehaviorConfigBuilder{
+ parent: b,
+ config: &b.config.Behavior,
}
}
-// ForColumn returns a builder for configuring a specific column across all sections.
+// ForColumn returns a ColumnConfigBuilder for column-specific configuration
func (b *ConfigBuilder) ForColumn(col int) *ColumnConfigBuilder {
return &ColumnConfigBuilder{
parent: b,
@@ -91,22 +73,17 @@ func (b *ConfigBuilder) ForColumn(col int) *ColumnConfigBuilder {
}
}
-// Header returns a builder for advanced configuration of the header section.
-func (b *ConfigBuilder) Header() *HeaderConfigBuilder {
- return &HeaderConfigBuilder{
- parent: b,
- config: &b.config.Header,
- section: "header",
- }
+// WithTrimSpace enables or disables automatic trimming of leading/trailing spaces.
+// Ignored in streaming mode.
+func (b *ConfigBuilder) WithTrimSpace(state tw.State) *ConfigBuilder {
+ b.config.Behavior.TrimSpace = state
+ return b
}
-// Row returns a builder for advanced configuration of the row section.
-func (b *ConfigBuilder) Row() *RowConfigBuilder {
- return &RowConfigBuilder{
- parent: b,
- config: &b.config.Row,
- section: "row",
- }
+// WithDebug enables/disables debug logging
+func (b *ConfigBuilder) WithDebug(debug bool) *ConfigBuilder {
+ b.config.Debug = debug
+ return b
}
// WithAutoHide enables or disables automatic hiding of empty columns (ignored in streaming mode).
@@ -115,19 +92,13 @@ func (b *ConfigBuilder) WithAutoHide(state tw.State) *ConfigBuilder {
return b
}
-// WithDebug enables or disables debug logging for the table.
-func (b *ConfigBuilder) WithDebug(debug bool) *ConfigBuilder {
- b.config.Debug = debug
- return b
-}
-
// WithFooterAlignment sets the text alignment for all footer cells.
// Invalid alignments are ignored.
func (b *ConfigBuilder) WithFooterAlignment(align tw.Align) *ConfigBuilder {
if align != tw.AlignLeft && align != tw.AlignRight && align != tw.AlignCenter && align != tw.AlignNone {
return b
}
- b.config.Footer.Formatting.Alignment = align
+ b.config.Footer.Alignment.Global = align
return b
}
@@ -179,7 +150,7 @@ func (b *ConfigBuilder) WithHeaderAlignment(align tw.Align) *ConfigBuilder {
if align != tw.AlignLeft && align != tw.AlignRight && align != tw.AlignCenter && align != tw.AlignNone {
return b
}
- b.config.Header.Formatting.Alignment = align
+ b.config.Header.Alignment.Global = align
return b
}
@@ -242,7 +213,7 @@ func (b *ConfigBuilder) WithRowAlignment(align tw.Align) *ConfigBuilder {
if align != tw.AlignLeft && align != tw.AlignRight && align != tw.AlignCenter && align != tw.AlignNone {
return b
}
- b.config.Row.Formatting.Alignment = align
+ b.config.Row.Alignment.Global = align
return b
}
@@ -288,864 +259,702 @@ func (b *ConfigBuilder) WithRowMergeMode(mergeMode int) *ConfigBuilder {
return b
}
-// WithTrimSpace enables or disables automatic trimming of leading/trailing spaces.
-// Ignored in streaming mode.
-func (b *ConfigBuilder) WithTrimSpace(state tw.State) *ConfigBuilder {
- b.config.Behavior.TrimSpace = state
- return b
+// HeaderConfigBuilder configures header settings
+type HeaderConfigBuilder struct {
+ parent *ConfigBuilder
+ config *tw.CellConfig
}
-// FooterConfigBuilder provides advanced configuration options for the footer section.
-type FooterConfigBuilder struct {
- parent *ConfigBuilder // Reference to the parent ConfigBuilder
- config *tw.CellConfig // Footer configuration being modified
- section string // Section name for logging/debugging
+// Build returns the parent ConfigBuilder
+func (h *HeaderConfigBuilder) Build() *ConfigBuilder {
+ return h.parent
}
-// Build returns the parent ConfigBuilder for chaining.
-func (f *FooterConfigBuilder) Build() *ConfigBuilder {
- return f.parent
+// Alignment returns an AlignmentConfigBuilder for header alignment
+func (h *HeaderConfigBuilder) Alignment() *AlignmentConfigBuilder {
+ return &AlignmentConfigBuilder{
+ parent: h.parent,
+ config: &h.config.Alignment,
+ section: "header",
+ }
}
-// Formatting returns a builder for configuring footer formatting settings.
-func (f *FooterConfigBuilder) Formatting() *FooterFormattingBuilder {
- return &FooterFormattingBuilder{
- parent: f,
- config: &f.config.Formatting,
- section: f.section,
+// Formatting returns a HeaderFormattingBuilder for header formatting
+func (h *HeaderConfigBuilder) Formatting() *HeaderFormattingBuilder {
+ return &HeaderFormattingBuilder{
+ parent: h,
+ config: &h.config.Formatting,
+ section: "header",
}
}
-// Padding returns a builder for configuring footer padding settings.
-func (f *FooterConfigBuilder) Padding() *FooterPaddingBuilder {
- return &FooterPaddingBuilder{
- parent: f,
- config: &f.config.Padding,
- section: f.section,
+// Padding returns a HeaderPaddingBuilder for header padding
+func (h *HeaderConfigBuilder) Padding() *HeaderPaddingBuilder {
+ return &HeaderPaddingBuilder{
+ parent: h,
+ config: &h.config.Padding,
+ section: "header",
}
}
-// FooterFormattingBuilder configures formatting options for the footer section.
-type FooterFormattingBuilder struct {
- parent *FooterConfigBuilder // Reference to the parent FooterConfigBuilder
- config *tw.CellFormatting // Formatting configuration being modified
- section string // Section name for logging/debugging
+// Filter returns a HeaderFilterBuilder for header filtering
+func (h *HeaderConfigBuilder) Filter() *HeaderFilterBuilder {
+ return &HeaderFilterBuilder{
+ parent: h,
+ config: &h.config.Filter,
+ section: "header",
+ }
}
-// Build returns the parent FooterConfigBuilder for chaining.
-func (ff *FooterFormattingBuilder) Build() *FooterConfigBuilder {
- return ff.parent
+// Callbacks returns a HeaderCallbacksBuilder for header callbacks
+func (h *HeaderConfigBuilder) Callbacks() *HeaderCallbacksBuilder {
+ return &HeaderCallbacksBuilder{
+ parent: h,
+ config: &h.config.Callbacks,
+ section: "header",
+ }
}
-// WithAlignment sets the text alignment for footer cells.
-// Invalid alignments are ignored.
-func (ff *FooterFormattingBuilder) WithAlignment(align tw.Align) *FooterFormattingBuilder {
- if align != tw.AlignLeft && align != tw.AlignRight && align != tw.AlignCenter && align != tw.AlignNone {
- return ff
+// RowConfigBuilder configures row settings
+type RowConfigBuilder struct {
+ parent *ConfigBuilder
+ config *tw.CellConfig
+}
+
+// Build returns the parent ConfigBuilder
+func (r *RowConfigBuilder) Build() *ConfigBuilder {
+ return r.parent
+}
+
+// Alignment returns an AlignmentConfigBuilder for row alignment
+func (r *RowConfigBuilder) Alignment() *AlignmentConfigBuilder {
+ return &AlignmentConfigBuilder{
+ parent: r.parent,
+ config: &r.config.Alignment,
+ section: "row",
}
- ff.config.Alignment = align
- return ff
}
-// WithAutoFormat enables or disables automatic formatting for footer cells.
-func (ff *FooterFormattingBuilder) WithAutoFormat(autoFormat tw.State) *FooterFormattingBuilder {
- ff.config.AutoFormat = autoFormat
- return ff
+// Formatting returns a RowFormattingBuilder for row formatting
+func (r *RowConfigBuilder) Formatting() *RowFormattingBuilder {
+ return &RowFormattingBuilder{
+ parent: r,
+ config: &r.config.Formatting,
+ section: "row",
+ }
}
-// WithAutoWrap sets the wrapping behavior for footer cells.
-// Invalid wrap modes are ignored.
-func (ff *FooterFormattingBuilder) WithAutoWrap(autoWrap int) *FooterFormattingBuilder {
- if autoWrap < tw.WrapNone || autoWrap > tw.WrapBreak {
- return ff
+// Padding returns a RowPaddingBuilder for row padding
+func (r *RowConfigBuilder) Padding() *RowPaddingBuilder {
+ return &RowPaddingBuilder{
+ parent: r,
+ config: &r.config.Padding,
+ section: "row",
}
- ff.config.AutoWrap = autoWrap
- return ff
}
-// WithMaxWidth sets the maximum content width for footer cells.
-// Negative values are ignored.
-//func (ff *FooterFormattingBuilder) WithMaxWidth(maxWidth int) *FooterFormattingBuilder {
-// if maxWidth < 0 {
-// return ff
-// }
-// ff.config.Foo = maxWidth
-// return ff
-//}
-
-// WithNewarkMode sets the merge behavior for footer cells (likely a typo, should be WithMergeMode).
-// Invalid merge modes are ignored.
-func (ff *FooterFormattingBuilder) WithNewarkMode(mergeMode int) *FooterFormattingBuilder {
- if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
- return ff
+// Filter returns a RowFilterBuilder for row filtering
+func (r *RowConfigBuilder) Filter() *RowFilterBuilder {
+ return &RowFilterBuilder{
+ parent: r,
+ config: &r.config.Filter,
+ section: "row",
}
- ff.config.MergeMode = mergeMode
- return ff
}
-// FooterPaddingBuilder configures padding options for the footer section.
-type FooterPaddingBuilder struct {
- parent *FooterConfigBuilder // Reference to the parent FooterConfigBuilder
- config *tw.CellPadding // Padding configuration being modified
- section string // Section name for logging/debugging
+// Callbacks returns a RowCallbacksBuilder for row callbacks
+func (r *RowConfigBuilder) Callbacks() *RowCallbacksBuilder {
+ return &RowCallbacksBuilder{
+ parent: r,
+ config: &r.config.Callbacks,
+ section: "row",
+ }
}
-// AddColumnPadding adds padding for a specific column in the footer.
-func (fp *FooterPaddingBuilder) AddColumnPadding(padding tw.Padding) *FooterPaddingBuilder {
- fp.config.PerColumn = append(fp.config.PerColumn, padding)
- return fp
+// FooterConfigBuilder configures footer settings
+type FooterConfigBuilder struct {
+ parent *ConfigBuilder
+ config *tw.CellConfig
}
-// Build returns the parent FooterConfigBuilder for chaining.
-func (fp *FooterPaddingBuilder) Build() *FooterConfigBuilder {
- return fp.parent
+// Build returns the parent ConfigBuilder
+func (f *FooterConfigBuilder) Build() *ConfigBuilder {
+ return f.parent
}
-// WithGlobal sets the global padding for all footer cells.
-func (fp *FooterPaddingBuilder) WithGlobal(padding tw.Padding) *FooterPaddingBuilder {
- fp.config.Global = padding
- return fp
+// Alignment returns an AlignmentConfigBuilder for footer alignment
+func (f *FooterConfigBuilder) Alignment() *AlignmentConfigBuilder {
+ return &AlignmentConfigBuilder{
+ parent: f.parent,
+ config: &f.config.Alignment,
+ section: "footer",
+ }
}
-// WithPerColumn sets per-column padding for the footer.
-func (fp *FooterPaddingBuilder) WithPerColumn(padding []tw.Padding) *FooterPaddingBuilder {
- fp.config.PerColumn = padding
- return fp
+// Formatting returns a FooterFormattingBuilder for footer formatting
+func (f *FooterConfigBuilder) Formatting() *FooterFormattingBuilder {
+ return &FooterFormattingBuilder{
+ parent: f,
+ config: &f.config.Formatting,
+ section: "footer",
+ }
}
-// HeaderConfigBuilder provides advanced configuration options for the header section.
-type HeaderConfigBuilder struct {
- parent *ConfigBuilder // Reference to the parent ConfigBuilder
- config *tw.CellConfig // Header configuration being modified
- section string // Section name for logging/debugging
+// Padding returns a FooterPaddingBuilder for footer padding
+func (f *FooterConfigBuilder) Padding() *FooterPaddingBuilder {
+ return &FooterPaddingBuilder{
+ parent: f,
+ config: &f.config.Padding,
+ section: "footer",
+ }
}
-// Build returns the parent ConfigBuilder for chaining.
-func (h *HeaderConfigBuilder) Build() *ConfigBuilder {
- return h.parent
+// Filter returns a FooterFilterBuilder for footer filtering
+func (f *FooterConfigBuilder) Filter() *FooterFilterBuilder {
+ return &FooterFilterBuilder{
+ parent: f,
+ config: &f.config.Filter,
+ section: "footer",
+ }
}
-// Formatting returns a builder for configuring header formatting settings.
-func (h *HeaderConfigBuilder) Formatting() *HeaderFormattingBuilder {
- return &HeaderFormattingBuilder{
- parent: h,
- config: &h.config.Formatting,
- section: h.section,
+// Callbacks returns a FooterCallbacksBuilder for footer callbacks
+func (f *FooterConfigBuilder) Callbacks() *FooterCallbacksBuilder {
+ return &FooterCallbacksBuilder{
+ parent: f,
+ config: &f.config.Callbacks,
+ section: "footer",
}
}
-// Padding returns a builder for configuring header padding settings.
-func (h *HeaderConfigBuilder) Padding() *HeaderPaddingBuilder {
- return &HeaderPaddingBuilder{
- parent: h,
- config: &h.config.Padding,
- section: h.section,
+// AlignmentConfigBuilder configures alignment settings
+type AlignmentConfigBuilder struct {
+ parent *ConfigBuilder
+ config *tw.CellAlignment
+ section string
+}
+
+// Build returns the parent ConfigBuilder
+func (a *AlignmentConfigBuilder) Build() *ConfigBuilder {
+ return a.parent
+}
+
+// WithGlobal sets global alignment
+func (a *AlignmentConfigBuilder) WithGlobal(align tw.Align) *AlignmentConfigBuilder {
+ if err := align.Validate(); err == nil {
+ a.config.Global = align
}
+ return a
}
-// HeaderFormattingBuilder configures formatting options for the header section.
+// WithPerColumn sets per-column alignments
+func (a *AlignmentConfigBuilder) WithPerColumn(alignments []tw.Align) *AlignmentConfigBuilder {
+ if len(alignments) > 0 {
+ a.config.PerColumn = alignments
+ }
+ return a
+}
+
+// HeaderFormattingBuilder configures header formatting
type HeaderFormattingBuilder struct {
- parent *HeaderConfigBuilder // Reference to the parent HeaderConfigBuilder
- config *tw.CellFormatting // Formatting configuration being modified
- section string // Section name for logging/debugging
+ parent *HeaderConfigBuilder
+ config *tw.CellFormatting
+ section string
}
-// Build returns the parent HeaderConfigBuilder for chaining.
+// Build returns the parent HeaderConfigBuilder
func (hf *HeaderFormattingBuilder) Build() *HeaderConfigBuilder {
return hf.parent
}
-// WithAlignment sets the text alignment for header cells.
-// Invalid alignments are ignored.
-func (hf *HeaderFormattingBuilder) WithAlignment(align tw.Align) *HeaderFormattingBuilder {
- if align != tw.AlignLeft && align != tw.AlignRight && align != tw.AlignCenter && align != tw.AlignNone {
- return hf
- }
- hf.config.Alignment = align
- return hf
-}
-
-// WithAutoFormat enables or disables automatic formatting for header cells.
+// WithAutoFormat enables/disables auto formatting
func (hf *HeaderFormattingBuilder) WithAutoFormat(autoFormat tw.State) *HeaderFormattingBuilder {
hf.config.AutoFormat = autoFormat
return hf
}
-// WithAutoWrap sets the wrapping behavior for header cells.
-// Invalid wrap modes are ignored.
+// WithAutoWrap sets auto wrap mode
func (hf *HeaderFormattingBuilder) WithAutoWrap(autoWrap int) *HeaderFormattingBuilder {
- if autoWrap < tw.WrapNone || autoWrap > tw.WrapBreak {
- return hf
+ if autoWrap >= tw.WrapNone && autoWrap <= tw.WrapBreak {
+ hf.config.AutoWrap = autoWrap
}
- hf.config.AutoWrap = autoWrap
return hf
}
-// WithMaxWidth sets the maximum content width for header cells.
-// Negative values are ignored.
-//func (hf *HeaderFormattingBuilder) WithMaxWidth(maxWidth int) *HeaderFormattingBuilder {
-// if maxWidth < 0 {
-// return hf
-// }
-// hf.config.MaxWidth = maxWidth
-// return hf
-//}
-
-// WithMergeMode sets the merge behavior for header cells.
-// Invalid merge modes are ignored.
+// WithMergeMode sets merge mode
func (hf *HeaderFormattingBuilder) WithMergeMode(mergeMode int) *HeaderFormattingBuilder {
- if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
- return hf
+ if mergeMode >= tw.MergeNone && mergeMode <= tw.MergeHierarchical {
+ hf.config.MergeMode = mergeMode
}
- hf.config.MergeMode = mergeMode
return hf
}
-// HeaderPaddingBuilder configures padding options for the header section.
-type HeaderPaddingBuilder struct {
- parent *HeaderConfigBuilder // Reference to the parent HeaderConfigBuilder
- config *tw.CellPadding // Padding configuration being modified
- section string // Section name for logging/debugging
+// RowFormattingBuilder configures row formatting
+type RowFormattingBuilder struct {
+ parent *RowConfigBuilder
+ config *tw.CellFormatting
+ section string
}
-// AddColumnPadding adds padding for a specific column in the header.
-func (hp *HeaderPaddingBuilder) AddColumnPadding(padding tw.Padding) *HeaderPaddingBuilder {
- hp.config.PerColumn = append(hp.config.PerColumn, padding)
- return hp
+// Build returns the parent RowConfigBuilder
+func (rf *RowFormattingBuilder) Build() *RowConfigBuilder {
+ return rf.parent
}
-// Build returns the parent HeaderConfigBuilder for chaining.
-func (hp *HeaderPaddingBuilder) Build() *HeaderConfigBuilder {
- return hp.parent
+// WithAutoFormat enables/disables auto formatting
+func (rf *RowFormattingBuilder) WithAutoFormat(autoFormat tw.State) *RowFormattingBuilder {
+ rf.config.AutoFormat = autoFormat
+ return rf
}
-// WithGlobal sets the global padding for all header cells.
-func (hp *HeaderPaddingBuilder) WithGlobal(padding tw.Padding) *HeaderPaddingBuilder {
- hp.config.Global = padding
- return hp
+// WithAutoWrap sets auto wrap mode
+func (rf *RowFormattingBuilder) WithAutoWrap(autoWrap int) *RowFormattingBuilder {
+ if autoWrap >= tw.WrapNone && autoWrap <= tw.WrapBreak {
+ rf.config.AutoWrap = autoWrap
+ }
+ return rf
}
-// WithPerColumn sets per-column padding for the header.
-func (hp *HeaderPaddingBuilder) WithPerColumn(padding []tw.Padding) *HeaderPaddingBuilder {
- hp.config.PerColumn = padding
- return hp
+// WithMergeMode sets merge mode
+func (rf *RowFormattingBuilder) WithMergeMode(mergeMode int) *RowFormattingBuilder {
+ if mergeMode >= tw.MergeNone && mergeMode <= tw.MergeHierarchical {
+ rf.config.MergeMode = mergeMode
+ }
+ return rf
}
-// Option defines a function type for configuring a Table instance.
-type Option func(target *Table)
-
-// RowConfigBuilder provides advanced configuration options for the row section.
-type RowConfigBuilder struct {
- parent *ConfigBuilder // Reference to the parent ConfigBuilder
- config *tw.CellConfig // Row configuration being modified
- section string // Section name for logging/debugging
+// FooterFormattingBuilder configures footer formatting
+type FooterFormattingBuilder struct {
+ parent *FooterConfigBuilder
+ config *tw.CellFormatting
+ section string
}
-// Build returns the parent ConfigBuilder for chaining.
-func (r *RowConfigBuilder) Build() *ConfigBuilder {
- return r.parent
+// Build returns the parent FooterConfigBuilder
+func (ff *FooterFormattingBuilder) Build() *FooterConfigBuilder {
+ return ff.parent
}
-// Formatting returns a builder for configuring row formatting settings.
-func (r *RowConfigBuilder) Formatting() *RowFormattingBuilder {
- return &RowFormattingBuilder{
- parent: r,
- config: &r.config.Formatting,
- section: r.section,
- }
+// WithAutoFormat enables/disables auto formatting
+func (ff *FooterFormattingBuilder) WithAutoFormat(autoFormat tw.State) *FooterFormattingBuilder {
+ ff.config.AutoFormat = autoFormat
+ return ff
}
-// Padding returns a builder for configuring row padding settings.
-func (r *RowConfigBuilder) Padding() *RowPaddingBuilder {
- return &RowPaddingBuilder{
- parent: r,
- config: &r.config.Padding,
- section: r.section,
+// WithAutoWrap sets auto wrap mode
+func (ff *FooterFormattingBuilder) WithAutoWrap(autoWrap int) *FooterFormattingBuilder {
+ if autoWrap >= tw.WrapNone && autoWrap <= tw.WrapBreak {
+ ff.config.AutoWrap = autoWrap
}
+ return ff
}
-// RowFormattingBuilder configures formatting options for the row section.
-type RowFormattingBuilder struct {
- parent *RowConfigBuilder // Reference to the parent RowConfigBuilder
- config *tw.CellFormatting // Formatting configuration being modified
- section string // Section name for logging/debugging
+// WithMergeMode sets merge mode
+func (ff *FooterFormattingBuilder) WithMergeMode(mergeMode int) *FooterFormattingBuilder {
+ if mergeMode >= tw.MergeNone && mergeMode <= tw.MergeHierarchical {
+ ff.config.MergeMode = mergeMode
+ }
+ return ff
}
-// Build returns the parent RowConfigBuilder for chaining.
-func (rf *RowFormattingBuilder) Build() *RowConfigBuilder {
- return rf.parent
+// HeaderPaddingBuilder configures header padding
+type HeaderPaddingBuilder struct {
+ parent *HeaderConfigBuilder
+ config *tw.CellPadding
+ section string
}
-// WithAlignment sets the text alignment for row cells.
-// Invalid alignments are ignored.
-func (rf *RowFormattingBuilder) WithAlignment(align tw.Align) *RowFormattingBuilder {
- if align != tw.AlignLeft && align != tw.AlignRight && align != tw.AlignCenter && align != tw.AlignNone {
- return rf
- }
- rf.config.Alignment = align
- return rf
+// Build returns the parent HeaderConfigBuilder
+func (hp *HeaderPaddingBuilder) Build() *HeaderConfigBuilder {
+ return hp.parent
}
-// WithAutoFormat enables or disables automatic formatting for row cells.
-func (rf *RowFormattingBuilder) WithAutoFormat(autoFormat tw.State) *RowFormattingBuilder {
- rf.config.AutoFormat = autoFormat
- return rf
+// WithGlobal sets global padding
+func (hp *HeaderPaddingBuilder) WithGlobal(padding tw.Padding) *HeaderPaddingBuilder {
+ hp.config.Global = padding
+ return hp
}
-// WithAutoWrap sets the wrapping behavior for row cells.
-// Invalid wrap modes are ignored.
-func (rf *RowFormattingBuilder) WithAutoWrap(autoWrap int) *RowFormattingBuilder {
- if autoWrap < tw.WrapNone || autoWrap > tw.WrapBreak {
- return rf
- }
- rf.config.AutoWrap = autoWrap
- return rf
+// WithPerColumn sets per-column padding
+func (hp *HeaderPaddingBuilder) WithPerColumn(padding []tw.Padding) *HeaderPaddingBuilder {
+ hp.config.PerColumn = padding
+ return hp
}
-// WithMergeMode sets the merge behavior for row cells.
-// Invalid merge modes are ignored.
-func (rf *RowFormattingBuilder) WithMergeMode(mergeMode int) *RowFormattingBuilder {
- if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
- return rf
- }
- rf.config.MergeMode = mergeMode
- return rf
+// AddColumnPadding adds padding for a specific column in the header
+func (hp *HeaderPaddingBuilder) AddColumnPadding(padding tw.Padding) *HeaderPaddingBuilder {
+ hp.config.PerColumn = append(hp.config.PerColumn, padding)
+ return hp
}
-// RowPaddingBuilder configures padding options for the row section.
+// RowPaddingBuilder configures row padding
type RowPaddingBuilder struct {
- parent *RowConfigBuilder // Reference to the parent RowConfigBuilder
- config *tw.CellPadding // Padding configuration being modified
- section string // Section name for logging/debugging
+ parent *RowConfigBuilder
+ config *tw.CellPadding
+ section string
}
-// AddColumnPadding adds padding for a specific column in the rows.
-func (rp *RowPaddingBuilder) AddColumnPadding(padding tw.Padding) *RowPaddingBuilder {
- rp.config.PerColumn = append(rp.config.PerColumn, padding)
- return rp
-}
-
-// Build returns the parent RowConfigBuilder for chaining.
+// Build returns the parent RowConfigBuilder
func (rp *RowPaddingBuilder) Build() *RowConfigBuilder {
return rp.parent
}
-// WithGlobal sets the global padding for all row cells.
+// WithGlobal sets global padding
func (rp *RowPaddingBuilder) WithGlobal(padding tw.Padding) *RowPaddingBuilder {
rp.config.Global = padding
return rp
}
-// WithPerColumn sets per-column padding for the rows.
+// WithPerColumn sets per-column padding
func (rp *RowPaddingBuilder) WithPerColumn(padding []tw.Padding) *RowPaddingBuilder {
rp.config.PerColumn = padding
return rp
}
-// NewConfigBuilder creates a new ConfigBuilder initialized with default settings.
-func NewConfigBuilder() *ConfigBuilder {
- return &ConfigBuilder{
- config: defaultConfig(),
- }
+// AddColumnPadding adds padding for a specific column in the rows
+func (rp *RowPaddingBuilder) AddColumnPadding(padding tw.Padding) *RowPaddingBuilder {
+ rp.config.PerColumn = append(rp.config.PerColumn, padding)
+ return rp
}
-// NewWriter creates a new table with default settings for backward compatibility.
-// It logs the creation if debugging is enabled.
-func NewWriter(w io.Writer) *Table {
- t := NewTable(w)
- if t.logger != nil {
- t.logger.Debug("NewWriter created buffered Table")
- }
- return t
+// FooterPaddingBuilder configures footer padding
+type FooterPaddingBuilder struct {
+ parent *FooterConfigBuilder
+ config *tw.CellPadding
+ section string
}
-// 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)
- }
- }
+// Build returns the parent FooterConfigBuilder
+func (fp *FooterPaddingBuilder) Build() *FooterConfigBuilder {
+ return fp.parent
}
-// 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.Stream.Widths.Global = width
- if target.logger != nil {
- target.logger.Debugf("Option: WithColumnMax applied to Table: %v", width)
- }
- }
+// WithGlobal sets global padding
+func (fp *FooterPaddingBuilder) WithGlobal(padding tw.Padding) *FooterPaddingBuilder {
+ fp.config.Global = padding
+ return fp
}
-// WithTableMax sets a global maximum table width for the table
-// Negative values are ignored, and the change is logged if debugging is enabled.
-func WithTableMax(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)
- }
- }
+// WithPerColumn sets per-column padding
+func (fp *FooterPaddingBuilder) WithPerColumn(padding []tw.Padding) *FooterPaddingBuilder {
+ fp.config.PerColumn = padding
+ return fp
}
-// WithColumnWidths sets per-column widths for the table in streaming mode.
-// Negative widths are removed, and the change is logged if debugging is enabled.
-func WithColumnWidths(widths map[int]int) Option {
- return func(target *Table) {
- for k, v := range widths {
- if v < 0 {
- delete(widths, k)
- }
- }
- target.config.Stream.Widths.PerColumn = widths
- if target.logger != nil {
- target.logger.Debugf("Option: WithColumnWidths applied to Table: %v", widths)
- }
- }
+// AddColumnPadding adds padding for a specific column in the footer
+func (fp *FooterPaddingBuilder) AddColumnPadding(padding tw.Padding) *FooterPaddingBuilder {
+ fp.config.PerColumn = append(fp.config.PerColumn, padding)
+ return fp
}
-// 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)
- }
+// BehaviorConfigBuilder configures behavior settings
+type BehaviorConfigBuilder struct {
+ parent *ConfigBuilder
+ config *tw.Behavior
}
-// 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
- }
+// Build returns the parent ConfigBuilder
+func (bb *BehaviorConfigBuilder) Build() *ConfigBuilder {
+ return bb.parent
}
-// WithFooter sets the table footers by calling the Footer method.
-func WithFooter(footers []string) Option {
- return func(target *Table) {
- target.Footer(footers)
- }
+// WithAutoHide enables/disables auto-hide
+func (bb *BehaviorConfigBuilder) WithAutoHide(state tw.State) *BehaviorConfigBuilder {
+ bb.config.AutoHide = state
+ return bb
}
-// 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.")
- }
- }
+// WithTrimSpace enables/disables trim space
+func (bb *BehaviorConfigBuilder) WithTrimSpace(state tw.State) *BehaviorConfigBuilder {
+ bb.config.TrimSpace = state
+ return bb
}
-// WithFooterMergeMode sets the merge mode for footer cells.
-// Invalid merge modes are ignored, and the change is logged if debugging is enabled.
-func WithFooterMergeMode(mergeMode int) Option {
- return func(target *Table) {
- if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
- return
- }
- target.config.Footer.Formatting.MergeMode = mergeMode
- if target.logger != nil {
- target.logger.Debugf("Option: WithFooterMergeMode applied to Table: %v", mergeMode)
- }
- }
+// WithHeaderHide enables/disables header visibility
+func (bb *BehaviorConfigBuilder) WithHeaderHide(state tw.State) *BehaviorConfigBuilder {
+ bb.config.Header.Hide = state
+ return bb
}
-// WithHeader sets the table headers by calling the Header method.
-func WithHeader(headers []string) Option {
- return func(target *Table) {
- target.Header(headers)
- }
+// WithFooterHide enables/disables footer visibility
+func (bb *BehaviorConfigBuilder) WithFooterHide(state tw.State) *BehaviorConfigBuilder {
+ bb.config.Footer.Hide = state
+ return bb
+}
+
+// WithCompactMerge enables/disables compact width optimization for merged cells
+func (bb *BehaviorConfigBuilder) WithCompactMerge(state tw.State) *BehaviorConfigBuilder {
+ bb.config.Compact.Merge = state
+ return bb
+}
+
+// ColumnConfigBuilder configures column-specific settings
+type ColumnConfigBuilder struct {
+ parent *ConfigBuilder
+ col int
+}
+
+// Build returns the parent ConfigBuilder
+func (c *ColumnConfigBuilder) Build() *ConfigBuilder {
+ return c.parent
}
-// 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
+// WithAlignment sets alignment for the column
+func (c *ColumnConfigBuilder) WithAlignment(align tw.Align) *ColumnConfigBuilder {
+ if err := align.Validate(); err == nil {
+ // Ensure slices are large enough
+ if len(c.parent.config.Header.Alignment.PerColumn) <= c.col {
+ newAligns := make([]tw.Align, c.col+1)
+ copy(newAligns, c.parent.config.Header.Alignment.PerColumn)
+ c.parent.config.Header.Alignment.PerColumn = newAligns
}
- target.config.Header.Formatting.Alignment = align
- if target.logger != nil {
- target.logger.Debugf("Option: WithHeaderAlignment applied to Table: %v", align)
+ c.parent.config.Header.Alignment.PerColumn[c.col] = align
+
+ if len(c.parent.config.Row.Alignment.PerColumn) <= c.col {
+ newAligns := make([]tw.Align, c.col+1)
+ copy(newAligns, c.parent.config.Row.Alignment.PerColumn)
+ c.parent.config.Row.Alignment.PerColumn = newAligns
}
- }
-}
+ c.parent.config.Row.Alignment.PerColumn[c.col] = align
-// 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.")
+ if len(c.parent.config.Footer.Alignment.PerColumn) <= c.col {
+ newAligns := make([]tw.Align, c.col+1)
+ copy(newAligns, c.parent.config.Footer.Alignment.PerColumn)
+ c.parent.config.Footer.Alignment.PerColumn = newAligns
}
+ c.parent.config.Footer.Alignment.PerColumn[c.col] = align
}
+ return c
}
-// 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)
- }
+// WithMaxWidth sets max width for the column
+func (c *ColumnConfigBuilder) WithMaxWidth(width int) *ColumnConfigBuilder {
+ if width >= 0 {
+ // Initialize maps if needed
+ if c.parent.config.Header.ColMaxWidths.PerColumn == nil {
+ c.parent.config.Header.ColMaxWidths.PerColumn = make(tw.Mapper[int, int])
+ c.parent.config.Row.ColMaxWidths.PerColumn = make(tw.Mapper[int, int])
+ c.parent.config.Footer.ColMaxWidths.PerColumn = make(tw.Mapper[int, int])
}
+ c.parent.config.Header.ColMaxWidths.PerColumn[c.col] = width
+ c.parent.config.Row.ColMaxWidths.PerColumn[c.col] = width
+ c.parent.config.Footer.ColMaxWidths.PerColumn[c.col] = width
}
+ return c
}
-// 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)
- }
- }
+// HeaderFilterBuilder configures header filtering
+type HeaderFilterBuilder struct {
+ parent *HeaderConfigBuilder
+ config *tw.CellFilter
+ section string
}
-// 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.")
- }
- }
+// Build returns the parent HeaderConfigBuilder
+func (hf *HeaderFilterBuilder) Build() *HeaderConfigBuilder {
+ return hf.parent
}
-// 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)
- }
+// WithGlobal sets the global filter function for the header
+func (hf *HeaderFilterBuilder) WithGlobal(filter func([]string) []string) *HeaderFilterBuilder {
+ if filter != nil {
+ hf.config.Global = filter
}
+ return hf
}
-// 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.")
- }
+// WithPerColumn sets per-column filter functions for the header
+func (hf *HeaderFilterBuilder) WithPerColumn(filters []func(string) string) *HeaderFilterBuilder {
+ if len(filters) > 0 {
+ hf.config.PerColumn = filters
}
+ return hf
}
-// 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.stringerCacheMu.Lock()
- t.stringerCache = make(map[reflect.Type]reflect.Value)
- t.stringerCacheMu.Unlock()
- t.logger.Debug("Stringer updated, cache cleared")
+// AddColumnFilter adds a filter function for a specific column in the header
+func (hf *HeaderFilterBuilder) AddColumnFilter(filter func(string) string) *HeaderFilterBuilder {
+ if filter != nil {
+ hf.config.PerColumn = append(hf.config.PerColumn, filter)
}
+ return hf
}
-// WithStringerCache enables caching for the stringer function.
-func WithStringerCache() Option {
- return func(t *Table) {
- t.stringerCacheEnabled = true
- }
+// RowFilterBuilder configures row filtering
+type RowFilterBuilder struct {
+ parent *RowConfigBuilder
+ config *tw.CellFilter
+ section string
}
-// WithSymbols sets the symbols used for table drawing and updates the renderer's configuration.
-// Logs the change if debugging is enabled.
-func WithSymbols(symbols tw.Symbols) Option {
- return func(target *Table) {
- if target.renderer != nil {
- cfg := target.renderer.Config()
- cfg.Symbols = symbols
- if target.logger != nil {
- target.logger.Debug("Option: WithSymbols applied to Table.")
- }
- }
- }
+// Build returns the parent RowConfigBuilder
+func (rf *RowFilterBuilder) Build() *RowConfigBuilder {
+ return rf.parent
}
-// 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)
- }
+// WithGlobal sets the global filter function for the rows
+func (rf *RowFilterBuilder) WithGlobal(filter func([]string) []string) *RowFilterBuilder {
+ if filter != nil {
+ rf.config.Global = filter
}
+ return rf
}
-func WithHeaderAutoFormat(state tw.State) Option {
- return func(target *Table) {
- target.config.Header.Formatting.AutoFormat = state
+// WithPerColumn sets per-column filter functions for the rows
+func (rf *RowFilterBuilder) WithPerColumn(filters []func(string) string) *RowFilterBuilder {
+ if len(filters) > 0 {
+ rf.config.PerColumn = filters
}
+ return rf
}
-// 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) // Fixed 'state' to 'control'
- }
+// AddColumnFilter adds a filter function for a specific column in the rows
+func (rf *RowFilterBuilder) AddColumnFilter(filter func(string) string) *RowFilterBuilder {
+ if filter != nil {
+ rf.config.PerColumn = append(rf.config.PerColumn, filter)
}
+ return rf
}
-// 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) // Fixed log message and 'state' to 'control'
- }
+// FooterFilterBuilder configures footer filtering
+type FooterFilterBuilder struct {
+ parent *FooterConfigBuilder
+ config *tw.CellFilter
+ section string
+}
+
+// Build returns the parent FooterConfigBuilder
+func (ff *FooterFilterBuilder) Build() *FooterConfigBuilder {
+ return ff.parent
+}
+
+// WithGlobal sets the global filter function for the footer
+func (ff *FooterFilterBuilder) WithGlobal(filter func([]string) []string) *FooterFilterBuilder {
+ if filter != nil {
+ ff.config.Global = filter
}
+ return ff
}
-// WithAlignment sets the default column alignment for the header, rows, and footer.
-func WithAlignment(alignment tw.Alignment) Option {
- return func(target *Table) {
- target.config.Header.ColumnAligns = alignment
- target.config.Row.ColumnAligns = alignment
- target.config.Footer.ColumnAligns = alignment
+// WithPerColumn sets per-column filter functions for the footer
+func (ff *FooterFilterBuilder) WithPerColumn(filters []func(string) string) *FooterFilterBuilder {
+ if len(filters) > 0 {
+ ff.config.PerColumn = filters
}
+ return ff
}
-// WithPadding sets the global padding for the header, rows, and footer.
-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
+// AddColumnFilter adds a filter function for a specific column in the footer
+func (ff *FooterFilterBuilder) AddColumnFilter(filter func(string) string) *FooterFilterBuilder {
+ if filter != nil {
+ ff.config.PerColumn = append(ff.config.PerColumn, filter)
}
+ return ff
}
-// 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.
-func WithRendition(rendition tw.Rendition) Option {
- return func(target *Table) {
- if target.renderer == nil {
- target.logger.Warn("Option: WithRendition: No renderer set on table.")
- return
- }
+// HeaderCallbacksBuilder configures header callbacks
+type HeaderCallbacksBuilder struct {
+ parent *HeaderConfigBuilder
+ config *tw.CellCallbacks
+ section string
+}
- if ru, ok := target.renderer.(tw.Renditioning); ok {
- ru.Rendition(rendition)
- target.logger.Debugf("Option: WithRendition: Applied to renderer via Renditioning.SetRendition(): %+v", rendition)
- } else {
- target.logger.Warnf("Option: WithRendition: Current renderer type %T does not implement tw.Renditioning. Rendition may not be applied as expected.", target.renderer)
- }
- }
+// Build returns the parent HeaderConfigBuilder
+func (hc *HeaderCallbacksBuilder) Build() *HeaderConfigBuilder {
+ return hc.parent
}
-// defaultConfig returns a default Config with sensible settings for headers, rows, footers, and behavior.
-func defaultConfig() Config {
- defaultPadding := tw.Padding{Left: tw.Space, Right: tw.Space, Top: tw.Empty, Bottom: tw.Empty}
- return Config{
- MaxWidth: 0,
- Header: tw.CellConfig{
- Formatting: tw.CellFormatting{
- AutoWrap: tw.WrapTruncate,
- Alignment: tw.AlignCenter,
- AutoFormat: tw.On,
- MergeMode: tw.MergeNone,
- },
- Padding: tw.CellPadding{
- Global: defaultPadding,
- },
- },
- Row: tw.CellConfig{
- Formatting: tw.CellFormatting{
- AutoWrap: tw.WrapNormal,
- Alignment: tw.AlignLeft,
- AutoFormat: tw.Off,
- MergeMode: tw.MergeNone,
- },
- Padding: tw.CellPadding{
- Global: defaultPadding,
- },
- },
- Footer: tw.CellConfig{
- Formatting: tw.CellFormatting{
- AutoWrap: tw.WrapNormal,
- Alignment: tw.AlignRight,
- AutoFormat: tw.Off,
- MergeMode: tw.MergeNone,
- },
- Padding: tw.CellPadding{
- Global: defaultPadding,
- },
- },
- Stream: tw.StreamConfig{},
- Debug: false,
- Behavior: Behavior{
- AutoHide: tw.Off,
- TrimSpace: tw.On,
- },
+// WithGlobal sets the global callback function for the header
+func (hc *HeaderCallbacksBuilder) WithGlobal(callback func()) *HeaderCallbacksBuilder {
+ if callback != nil {
+ hc.config.Global = callback
}
+ return hc
}
-// 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
+// WithPerColumn sets per-column callback functions for the header
+func (hc *HeaderCallbacksBuilder) WithPerColumn(callbacks []func()) *HeaderCallbacksBuilder {
+ if len(callbacks) > 0 {
+ hc.config.PerColumn = callbacks
}
- if src.ColMaxWidths.Global != 0 {
- dst.ColMaxWidths.Global = src.ColMaxWidths.Global
- }
- if src.Formatting.MergeMode != 0 {
- dst.Formatting.MergeMode = src.Formatting.MergeMode
+ return hc
+}
+
+// AddColumnCallback adds a callback function for a specific column in the header
+func (hc *HeaderCallbacksBuilder) AddColumnCallback(callback func()) *HeaderCallbacksBuilder {
+ if callback != nil {
+ hc.config.PerColumn = append(hc.config.PerColumn, callback)
}
+ return hc
+}
- dst.Formatting.AutoFormat = src.Formatting.AutoFormat
+// RowCallbacksBuilder configures row callbacks
+type RowCallbacksBuilder struct {
+ parent *RowConfigBuilder
+ config *tw.CellCallbacks
+ section string
+}
- if src.Padding.Global != (tw.Padding{}) {
- 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 != (tw.Padding{}) {
- 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
- }
- }
- }
- 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.Empty && align != tw.Skip {
- dst.ColumnAligns[i] = align
- }
- }
+// Build returns the parent RowConfigBuilder
+func (rc *RowCallbacksBuilder) Build() *RowConfigBuilder {
+ return rc.parent
+}
+
+// WithGlobal sets the global callback function for the rows
+func (rc *RowCallbacksBuilder) WithGlobal(callback func()) *RowCallbacksBuilder {
+ if callback != nil {
+ rc.config.Global = callback
}
- 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 rc
+}
+
+// WithPerColumn sets per-column callback functions for the rows
+func (rc *RowCallbacksBuilder) WithPerColumn(callbacks []func()) *RowCallbacksBuilder {
+ if len(callbacks) > 0 {
+ rc.config.PerColumn = callbacks
}
- return dst
+ return rc
}
-// 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
+// AddColumnCallback adds a callback function for a specific column in the rows
+func (rc *RowCallbacksBuilder) AddColumnCallback(callback func()) *RowCallbacksBuilder {
+ if callback != nil {
+ rc.config.PerColumn = append(rc.config.PerColumn, callback)
}
- dst.Debug = src.Debug || dst.Debug
- dst.Behavior.AutoHide = src.Behavior.AutoHide
- dst.Behavior.TrimSpace = src.Behavior.TrimSpace
- 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 rc
+}
- return dst
+// FooterCallbacksBuilder configures footer callbacks
+type FooterCallbacksBuilder struct {
+ parent *FooterConfigBuilder
+ config *tw.CellCallbacks
+ section string
}
-// 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
- }
- 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
- }
- }
+// Build returns the parent FooterConfigBuilder
+func (fc *FooterCallbacksBuilder) Build() *FooterConfigBuilder {
+ return fc.parent
+}
+
+// WithGlobal sets the global callback function for the footer
+func (fc *FooterCallbacksBuilder) WithGlobal(callback func()) *FooterCallbacksBuilder {
+ if callback != nil {
+ fc.config.Global = callback
}
- return dst
+ return fc
}
-// 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
+// WithPerColumn sets per-column callback functions for the footer
+func (fc *FooterCallbacksBuilder) WithPerColumn(callbacks []func()) *FooterCallbacksBuilder {
+ if len(callbacks) > 0 {
+ fc.config.PerColumn = callbacks
}
- padded := make([]string, numCols)
- copy(padded, line)
- for i := len(line); i < numCols; i++ {
- padded[i] = tw.Empty
+ return fc
+}
+
+// AddColumnCallback adds a callback function for a specific column in the footer
+func (fc *FooterCallbacksBuilder) AddColumnCallback(callback func()) *FooterCallbacksBuilder {
+ if callback != nil {
+ fc.config.PerColumn = append(fc.config.PerColumn, callback)
}
- return padded
+ return fc
}
diff --git a/vendor/github.com/olekukonko/tablewriter/deprecated.go b/vendor/github.com/olekukonko/tablewriter/deprecated.go
index 3958a25b3a..b61d507ac8 100644
--- a/vendor/github.com/olekukonko/tablewriter/deprecated.go
+++ b/vendor/github.com/olekukonko/tablewriter/deprecated.go
@@ -2,9 +2,27 @@ package tablewriter
import "github.com/olekukonko/tablewriter/tw"
-// Deprecated: WithBorders is no longer used.
-// Border control has been moved to the renderer, which now manages its own borders.
-// This Option has no effect on the Table and may be removed in future versions.
+// WithBorders configures the table's border settings by updating the renderer's border configuration.
+// This function is deprecated and will be removed in a future version.
+//
+// Deprecated: Use [WithRendition] to configure border settings for renderers that support
+// [tw.Renditioning], or update the renderer's [tw.RenderConfig] directly via its Config() method.
+// This function has no effect if no renderer is set on the table.
+//
+// Example migration:
+//
+// // Old (deprecated)
+// table.Options(WithBorders(tw.Border{Top: true, Bottom: true}))
+// // New (recommended)
+// table.Options(WithRendition(tw.Rendition{Borders: tw.Border{Top: true, Bottom: true}}))
+//
+// Parameters:
+// - borders: The [tw.Border] configuration to apply to the renderer's borders.
+//
+// Returns:
+//
+// An [Option] that updates the renderer's border settings if a renderer is set.
+// Logs a debug message if debugging is enabled and a renderer is present.
func WithBorders(borders tw.Border) Option {
return func(target *Table) {
if target.renderer != nil {
@@ -17,16 +35,55 @@ func WithBorders(borders tw.Border) Option {
}
}
-// Deprecated: WithBorders is no longer supported.
-// Use [tw.Behavior] directly to configure border settings.
+// Behavior is an alias for [tw.Behavior] to configure table behavior settings.
+// This type is deprecated and will be removed in a future version.
+//
+// Deprecated: Use [tw.Behavior] directly to configure settings such as auto-hiding empty
+// columns, trimming spaces, or controlling header/footer visibility.
+//
+// Example migration:
+//
+// // Old (deprecated)
+// var b tablewriter.Behavior = tablewriter.Behavior{AutoHide: tw.On}
+// // New (recommended)
+// var b tw.Behavior = tw.Behavior{AutoHide: tw.On}
type Behavior tw.Behavior
-// Deprecated: WithRendererSettings i sno longer supported.
+// Settings is an alias for [tw.Settings] to configure renderer settings.
+// This type is deprecated and will be removed in a future version.
+//
+// Deprecated: Use [tw.Settings] directly to configure renderer settings, such as
+// separators and line styles.
+//
+// Example migration:
+//
+// // Old (deprecated)
+// var s tablewriter.Settings = tablewriter.Settings{Separator: "|"}
+// // New (recommended)
+// var s tw.Settings = tw.Settings{Separator: "|"}
type Settings tw.Settings
-// WithRendererSettings updates the renderer's settings (e.g., separators, lines).
-// Render setting has move to renders directly
-// you can also use WithRendition for renders that have rendition support
+// WithRendererSettings updates the renderer's settings, such as separators and line styles.
+// This function is deprecated and will be removed in a future version.
+//
+// Deprecated: Use [WithRendition] to update renderer settings for renderers that implement
+// [tw.Renditioning], or configure the renderer's [tw.Settings] directly via its
+// [tw.Renderer.Config] method. This function has no effect if no renderer is set.
+//
+// Example migration:
+//
+// // Old (deprecated)
+// table.Options(WithRendererSettings(tw.Settings{Separator: "|"}))
+// // New (recommended)
+// table.Options(WithRendition(tw.Rendition{Settings: tw.Settings{Separator: "|"}}))
+//
+// Parameters:
+// - settings: The [tw.Settings] configuration to apply to the renderer.
+//
+// Returns:
+//
+// An [Option] that updates the renderer's settings if a renderer is set.
+// Logs a debug message if debugging is enabled and a renderer is present.
func WithRendererSettings(settings tw.Settings) Option {
return func(target *Table) {
if target.renderer != nil {
@@ -38,3 +95,124 @@ func WithRendererSettings(settings tw.Settings) Option {
}
}
}
+
+// WithAlignment sets the text alignment for footer cells within the formatting configuration.
+// This method is deprecated and will be removed in the next version.
+//
+// Deprecated: Use [FooterConfigBuilder.Alignment] with [AlignmentConfigBuilder.WithGlobal]
+// or [AlignmentConfigBuilder.WithPerColumn] to configure footer alignments.
+// Alternatively, apply a complete [tw.CellAlignment] configuration using
+// [WithFooterAlignmentConfig].
+//
+// Example migration:
+//
+// // Old (deprecated)
+// builder.Footer().Formatting().WithAlignment(tw.AlignRight)
+// // New (recommended)
+// builder.Footer().Alignment().WithGlobal(tw.AlignRight)
+// // Or
+// table.Options(WithFooterAlignmentConfig(tw.CellAlignment{Global: tw.AlignRight}))
+//
+// Parameters:
+// - align: The [tw.Align] value to set for footer cells. Valid values are
+// [tw.AlignLeft], [tw.AlignRight], [tw.AlignCenter], and [tw.AlignNone].
+// Invalid alignments are ignored.
+//
+// Returns:
+//
+// The [FooterFormattingBuilder] instance for method chaining.
+func (ff *FooterFormattingBuilder) WithAlignment(align tw.Align) *FooterFormattingBuilder {
+ if align != tw.AlignLeft && align != tw.AlignRight && align != tw.AlignCenter && align != tw.AlignNone {
+ return ff
+ }
+ ff.config.Alignment = align
+ return ff
+}
+
+// WithAlignment sets the text alignment for header cells within the formatting configuration.
+// This method is deprecated and will be removed in the next version.
+//
+// Deprecated: Use [HeaderConfigBuilder.Alignment] with [AlignmentConfigBuilder.WithGlobal]
+// or [AlignmentConfigBuilder.WithPerColumn] to configure header alignments.
+// Alternatively, apply a complete [tw.CellAlignment] configuration using
+// [WithHeaderAlignmentConfig].
+//
+// Example migration:
+//
+// // Old (deprecated)
+// builder.Header().Formatting().WithAlignment(tw.AlignCenter)
+// // New (recommended)
+// builder.Header().Alignment().WithGlobal(tw.AlignCenter)
+// // Or
+// table.Options(WithHeaderAlignmentConfig(tw.CellAlignment{Global: tw.AlignCenter}))
+//
+// Parameters:
+// - align: The [tw.Align] value to set for header cells. Valid values are
+// [tw.AlignLeft], [tw.AlignRight], [tw.AlignCenter], and [tw.AlignNone].
+// Invalid alignments are ignored.
+//
+// Returns:
+//
+// The [HeaderFormattingBuilder] instance for method chaining.
+func (hf *HeaderFormattingBuilder) WithAlignment(align tw.Align) *HeaderFormattingBuilder {
+ if align != tw.AlignLeft && align != tw.AlignRight && align != tw.AlignCenter && align != tw.AlignNone {
+ return hf
+ }
+ hf.config.Alignment = align
+ return hf
+}
+
+// WithAlignment sets the text alignment for row cells within the formatting configuration.
+// This method is deprecated and will be removed in the next version.
+//
+// Deprecated: Use [RowConfigBuilder.Alignment] with [AlignmentConfigBuilder.WithGlobal]
+// or [AlignmentConfigBuilder.WithPerColumn] to configure row alignments.
+// Alternatively, apply a complete [tw.CellAlignment] configuration using
+// [WithRowAlignmentConfig].
+//
+// Example migration:
+//
+// // Old (deprecated)
+// builder.Row().Formatting().WithAlignment(tw.AlignLeft)
+// // New (recommended)
+// builder.Row().Alignment().WithGlobal(tw.AlignLeft)
+// // Or
+// table.Options(WithRowAlignmentConfig(tw.CellAlignment{Global: tw.AlignLeft}))
+//
+// Parameters:
+// - align: The [tw.Align] value to set for row cells. Valid values are
+// [tw.AlignLeft], [tw.AlignRight], [tw.AlignCenter], and [tw.AlignNone].
+// Invalid alignments are ignored.
+//
+// Returns:
+//
+// The [RowFormattingBuilder] instance for method chaining.
+func (rf *RowFormattingBuilder) WithAlignment(align tw.Align) *RowFormattingBuilder {
+ if align != tw.AlignLeft && align != tw.AlignRight && align != tw.AlignCenter && align != tw.AlignNone {
+ return rf
+ }
+ rf.config.Alignment = align
+ return rf
+}
+
+// WithTableMax sets the maximum width of the entire table in characters.
+// Negative values are ignored, and the change is logged if debugging is enabled.
+// The width constrains the table's rendering, potentially causing text wrapping or truncation
+// based on the configuration's wrapping settings (e.g., tw.WrapTruncate).
+// If debug logging is enabled via WithDebug(true), the applied width is logged.
+//
+// Deprecated: Use WithMaxWidth instead, which provides the same functionality with a clearer name
+// and consistent naming across the package. For example:
+//
+// tablewriter.NewTable(os.Stdout, tablewriter.WithMaxWidth(80))
+func WithTableMax(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)
+ }
+ }
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/option.go b/vendor/github.com/olekukonko/tablewriter/option.go
new file mode 100644
index 0000000000..1302462cd2
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/option.go
@@ -0,0 +1,859 @@
+package tablewriter
+
+import (
+ "github.com/olekukonko/ll"
+ "github.com/olekukonko/tablewriter/tw"
+ "reflect"
+)
+
+// 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)
+ }
+ }
+}
+
+// WithFooterMergeMode sets the merge mode for footer cells.
+// Invalid merge modes are ignored, and the change is logged if debugging is enabled.
+func WithFooterMergeMode(mergeMode int) Option {
+ return func(target *Table) {
+ if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
+ return
+ }
+ 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)
+ }
+ }
+}
+
+// WithHeaderMergeMode sets the merge mode for header cells.
+// Invalid merge modes are ignored, and the change is logged if debugging is enabled.
+func WithHeaderMergeMode(mergeMode int) Option {
+ return func(target *Table) {
+ if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
+ return
+ }
+ 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)
+ }
+ }
+}
+
+// WithRowMergeMode sets the merge mode for row cells.
+// Invalid merge modes are ignored, and the change is logged if debugging is enabled.
+func WithRowMergeMode(mergeMode int) Option {
+ return func(target *Table) {
+ if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
+ return
+ }
+ 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.stringerCacheMu.Lock()
+ t.stringerCache = make(map[reflect.Type]reflect.Value)
+ t.stringerCacheMu.Unlock()
+ if t.logger != nil {
+ t.logger.Debug("Stringer updated, cache cleared")
+ }
+ }
+}
+
+// WithStringerCache enables caching for the stringer function.
+// Logs the change if debugging is enabled.
+func WithStringerCache() Option {
+ return func(t *Table) {
+ t.stringerCacheEnabled = true
+ if t.logger != nil {
+ t.logger.Debug("Option: WithStringerCache 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)
+ }
+ }
+}
+
+// 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)
+ }
+ }
+ }
+}
+
+// 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)
+ }
+ }
+ }
+ }
+}
+
+// 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,
+ },
+ 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,
+ },
+ 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,
+ },
+ Padding: tw.CellPadding{
+ Global: tw.PaddingDefault,
+ },
+ Alignment: tw.CellAlignment{
+ Global: tw.AlignRight,
+ PerColumn: []tw.Align{},
+ },
+ },
+ Stream: tw.StreamConfig{},
+ Debug: false,
+ Behavior: tw.Behavior{
+ AutoHide: tw.Off,
+ TrimSpace: tw.On,
+ },
+ }
+}
+
+// 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
+ }
+ if src.Formatting.MergeMode != 0 {
+ dst.Formatting.MergeMode = src.Formatting.MergeMode
+ }
+
+ 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.Compact = src.Behavior.Compact
+ dst.Behavior.Header = src.Behavior.Header
+ dst.Behavior.Footer = src.Behavior.Footer
+
+ 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
+ }
+ 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
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/renderer/markdown.go b/vendor/github.com/olekukonko/tablewriter/renderer/markdown.go
index d9435f3500..525e633842 100644
--- a/vendor/github.com/olekukonko/tablewriter/renderer/markdown.go
+++ b/vendor/github.com/olekukonko/tablewriter/renderer/markdown.go
@@ -140,10 +140,10 @@ func (m *Markdown) resolveAlignment(ctx tw.Formatting) tw.Alignment {
// build default alignment
for i := 0; i < total; i++ {
- m.alignment = append(m.alignment, tw.AlignLeft)
+ m.alignment = append(m.alignment, tw.AlignNone) // Default to AlignNone
}
- // add per colum alignment if it exits
+ // add per column alignment if it exists
for i := 0; i < total; i++ {
m.alignment[i] = ctx.Row.Current[i].Align
}
@@ -255,9 +255,10 @@ func (m *Markdown) formatSeparator(width int, align tw.Align) string {
sb.WriteRune(':')
sb.WriteString(strings.Repeat("-", targetWidth-2))
sb.WriteRune(':')
+ case tw.AlignNone:
+ sb.WriteString(strings.Repeat("-", targetWidth))
default:
- sb.WriteRune(':')
- sb.WriteString(strings.Repeat("-", targetWidth-1))
+ sb.WriteString(strings.Repeat("-", targetWidth)) // Fallback
}
result := sb.String()
@@ -321,12 +322,11 @@ func (m *Markdown) renderMarkdownLine(line []string, ctx tw.Formatting, isHeader
defaultPadding := tw.Padding{Left: tw.Space, Right: tw.Space}
if !ok {
-
cellCtx = tw.CellContext{
Data: tw.Empty, Align: align, Padding: defaultPadding,
Width: ctx.Row.Widths.Get(colIndex), Merge: tw.MergeState{},
}
- } else if cellCtx.Padding == (tw.Padding{}) {
+ } else if !cellCtx.Padding.Paddable() {
cellCtx.Padding = defaultPadding
}
@@ -339,18 +339,6 @@ func (m *Markdown) renderMarkdownLine(line []string, ctx tw.Formatting, isHeader
// Calculate width and span
span := 1
-
- if align == tw.AlignNone || align == tw.Empty {
- if ctx.Row.Position == tw.Header && !isHeaderSep {
- align = tw.AlignCenter
- } else if ctx.Row.Position == tw.Footer {
- align = tw.AlignRight
- } else {
- align = tw.AlignLeft
- }
- m.logger.Debugf("renderMarkdownLine: Col %d using default align '%s'", colIndex, align)
- }
-
visualWidth := 0
isHMergeStart := ok && cellCtx.Merge.Horizontal.Present && cellCtx.Merge.Horizontal.Start
if isHMergeStart {
@@ -383,10 +371,11 @@ func (m *Markdown) renderMarkdownLine(line []string, ctx tw.Formatting, isHeader
var formattedSegment string
if isHeaderSep {
// Use header's alignment from ctx.Row.Previous
- headerAlign := tw.AlignCenter // Default for headers
+ headerAlign := align
if headerCellCtx, headerOK := ctx.Row.Previous[colIndex]; headerOK {
headerAlign = headerCellCtx.Align
- if headerAlign == tw.AlignNone || headerAlign == tw.Empty {
+ // Preserve tw.AlignNone for separator
+ if headerAlign != tw.AlignNone && (headerAlign == tw.Empty || headerAlign == tw.Skip) {
headerAlign = tw.AlignCenter
}
}
@@ -403,6 +392,16 @@ func (m *Markdown) renderMarkdownLine(line []string, ctx tw.Formatting, isHeader
rowAlign = headerCellCtx.Align
}
}
+ if rowAlign == tw.AlignNone || rowAlign == tw.Empty {
+ if ctx.Row.Position == tw.Header {
+ rowAlign = tw.AlignCenter
+ } else if ctx.Row.Position == tw.Footer {
+ rowAlign = tw.AlignRight
+ } else {
+ rowAlign = tw.AlignLeft
+ }
+ m.logger.Debugf("renderMarkdownLine: Col %d using default align '%s'", colIndex, rowAlign)
+ }
formattedSegment = m.formatCell(content, visualWidth, rowAlign, cellCtx.Padding)
}
output.WriteString(formattedSegment)
diff --git a/vendor/github.com/olekukonko/tablewriter/renderer/ocean.go b/vendor/github.com/olekukonko/tablewriter/renderer/ocean.go
index 12f9d45c9f..58a6c9d6bb 100644
--- a/vendor/github.com/olekukonko/tablewriter/renderer/ocean.go
+++ b/vendor/github.com/olekukonko/tablewriter/renderer/ocean.go
@@ -340,7 +340,7 @@ func (o *Ocean) renderContentLine(ctx tw.Formatting, lineData []string) {
if cellCtx.Align.Validate() == nil && cellCtx.Align != tw.AlignNone {
align = cellCtx.Align
}
- if cellCtx.Padding != (tw.Padding{}) {
+ if cellCtx.Padding.Paddable() {
padding = cellCtx.Padding
}
} else if colIdx < len(lineData) {
diff --git a/vendor/github.com/olekukonko/tablewriter/stream.go b/vendor/github.com/olekukonko/tablewriter/stream.go
index a990c6e09b..1b230d04de 100644
--- a/vendor/github.com/olekukonko/tablewriter/stream.go
+++ b/vendor/github.com/olekukonko/tablewriter/stream.go
@@ -113,10 +113,10 @@ func (t *Table) Start() error {
// Calculate initial fixed widths if provided in StreamConfig.Widths
// These widths will be used for all subsequent rendering in streaming mode.
- if t.config.Stream.Widths.PerColumn != nil && t.config.Stream.Widths.PerColumn.Len() > 0 {
+ if t.config.Widths.PerColumn != nil && t.config.Widths.PerColumn.Len() > 0 {
// Use per-column stream widths if set
- t.logger.Debugf("Using per-column stream widths from StreamConfig: %v", t.config.Stream.Widths.PerColumn)
- t.streamWidths = t.config.Stream.Widths.PerColumn.Clone()
+ t.logger.Debugf("Using per-column stream widths from StreamConfig: %v", t.config.Widths.PerColumn)
+ t.streamWidths = t.config.Widths.PerColumn.Clone()
// Determine numCols from the highest index in PerColumn map
maxColIdx := -1
t.streamWidths.Each(func(col int, width int) {
@@ -139,14 +139,14 @@ func (t *Table) Start() error {
t.logger.Debugf("PerColumn widths map is effectively empty or contains only negative values, streamNumCols = 0.")
}
- } else if t.config.Stream.Widths.Global > 0 {
+ } else if t.config.Widths.Global > 0 {
// Global width is set, but we don't know the number of columns yet.
// Defer applying global width until the first data (Header or first Row) arrives.
// Store a placeholder or flag indicating global width should be used.
// The simple way for now: Keep streamWidths empty, signal the global width preference.
// The width calculation function called later will need to check StreamConfig.Widths.Global
// if streamWidths is empty.
- t.logger.Debugf("Global stream width %d set in StreamConfig. Will derive numCols from first data.", t.config.Stream.Widths.Global)
+ t.logger.Debugf("Global stream width %d set in StreamConfig. Will derive numCols from first data.", t.config.Widths.Global)
t.streamWidths = tw.NewMapper[int, int]() // Initialize as empty, will be populated later
// Note: No need to store Global width value here, it's available in t.config.Stream.Widths.Global
@@ -451,26 +451,26 @@ func (t *Table) streamBuildCellContexts(
// The paddingConfig should be the CellPadding config relevant to the sample data (Header/Row/Footer).
// Returns the determined number of columns.
// This function should only be called when t.streamWidths is currently empty.
-func (t *Table) streamCalculateWidths(sampleDataLines []string, sectionConfigForSampleData tw.CellConfig) int {
+func (t *Table) streamCalculateWidths(sampling []string, config tw.CellConfig) int {
if t.streamWidths != nil && t.streamWidths.Len() > 0 {
t.logger.Debug("streamCalculateWidths: Called when streaming widths are already set (%d columns). Reusing existing.", t.streamNumCols)
return t.streamNumCols
}
- t.logger.Debug("streamCalculateWidths: Calculating streaming widths. Sample data cells: %d. Using section config: %+v", len(sampleDataLines), sectionConfigForSampleData.Formatting)
+ t.logger.Debug("streamCalculateWidths: Calculating streaming widths. Sample data cells: %d. Using section config: %+v", len(sampling), config.Formatting)
determinedNumCols := 0
- if t.config.Stream.Widths.PerColumn != nil && t.config.Stream.Widths.PerColumn.Len() > 0 {
+ if t.config.Widths.PerColumn != nil && t.config.Widths.PerColumn.Len() > 0 {
maxColIdx := -1
- t.config.Stream.Widths.PerColumn.Each(func(col int, width int) {
+ t.config.Widths.PerColumn.Each(func(col int, width int) {
if col > maxColIdx {
maxColIdx = col
}
})
determinedNumCols = maxColIdx + 1
t.logger.Debug("streamCalculateWidths: Determined numCols (%d) from StreamConfig.Widths.PerColumn", determinedNumCols)
- } else if len(sampleDataLines) > 0 {
- determinedNumCols = len(sampleDataLines)
+ } else if len(sampling) > 0 {
+ determinedNumCols = len(sampling)
t.logger.Debug("streamCalculateWidths: Determined numCols (%d) from sample data length", determinedNumCols)
} else {
t.logger.Debug("streamCalculateWidths: Cannot determine numCols (no PerColumn config, no sample data)")
@@ -482,14 +482,14 @@ func (t *Table) streamCalculateWidths(sampleDataLines []string, sectionConfigFor
t.streamNumCols = determinedNumCols
t.streamWidths = tw.NewMapper[int, int]()
- // Use padding and autowrap from the provided sectionConfigForSampleData
- paddingForWidthCalc := sectionConfigForSampleData.Padding
- autoWrapForWidthCalc := sectionConfigForSampleData.Formatting.AutoWrap
+ // Use padding and autowrap from the provided config
+ paddingForWidthCalc := config.Padding
+ autoWrapForWidthCalc := config.Formatting.AutoWrap
- if t.config.Stream.Widths.PerColumn != nil && t.config.Stream.Widths.PerColumn.Len() > 0 {
+ if t.config.Widths.PerColumn != nil && t.config.Widths.PerColumn.Len() > 0 {
t.logger.Debug("streamCalculateWidths: Using widths from StreamConfig.Widths.PerColumn")
for i := 0; i < t.streamNumCols; i++ {
- width, ok := t.config.Stream.Widths.PerColumn.OK(i)
+ width, ok := t.config.Widths.PerColumn.OK(i)
if !ok {
width = 0
}
@@ -501,12 +501,12 @@ func (t *Table) streamCalculateWidths(sampleDataLines []string, sectionConfigFor
t.streamWidths.Set(i, width)
}
} else {
- // No PerColumn config, derive from sampleDataLines intelligently
+ // No PerColumn config, derive from sampling intelligently
t.logger.Debug("streamCalculateWidths: Intelligently deriving widths from sample data content and padding.")
tempRequiredWidths := tw.NewMapper[int, int]() // Widths from updateWidths (content + padding)
- if len(sampleDataLines) > 0 {
+ if len(sampling) > 0 {
// updateWidths calculates: DisplayWidth(content) + padLeft + padRight
- t.updateWidths(sampleDataLines, tempRequiredWidths, paddingForWidthCalc)
+ t.updateWidths(sampling, tempRequiredWidths, paddingForWidthCalc)
}
ellipsisWidthBuffer := 0
@@ -522,13 +522,13 @@ func (t *Table) streamCalculateWidths(sampleDataLines []string, sectionConfigFor
// We need to deconstruct it to apply logic to content_width first.
sampleContent := ""
- if i < len(sampleDataLines) {
- sampleContent = t.Trimmer(sampleDataLines[i])
+ if i < len(sampling) {
+ sampleContent = t.Trimmer(sampling[i])
}
sampleContentDisplayWidth := tw.DisplayWidth(sampleContent)
colPad := paddingForWidthCalc.Global
- if i < len(paddingForWidthCalc.PerColumn) && paddingForWidthCalc.PerColumn[i] != (tw.Padding{}) {
+ if i < len(paddingForWidthCalc.PerColumn) && paddingForWidthCalc.PerColumn[i].Paddable() {
colPad = paddingForWidthCalc.PerColumn[i]
}
currentPadLWidth := tw.DisplayWidth(colPad.Left)
@@ -584,8 +584,8 @@ func (t *Table) streamCalculateWidths(sampleDataLines []string, sectionConfigFor
}
// Apply Global Constraint (if t.config.Stream.Widths.Global > 0)
- if t.config.Stream.Widths.Global > 0 && t.streamNumCols > 0 {
- t.logger.Debug("streamCalculateWidths: Applying global stream width constraint %d", t.config.Stream.Widths.Global)
+ if t.config.Widths.Global > 0 && t.streamNumCols > 0 {
+ t.logger.Debug("streamCalculateWidths: Applying global stream width constraint %d", t.config.Widths.Global)
currentTotalColumnWidthsSum := 0
t.streamWidths.Each(func(_ int, w int) {
currentTotalColumnWidthsSum += w
@@ -606,11 +606,11 @@ func (t *Table) streamCalculateWidths(sampleDataLines []string, sectionConfigFor
totalWidthIncludingSeparators += (t.streamNumCols - 1) * separatorWidth
}
- if t.config.Stream.Widths.Global < totalWidthIncludingSeparators && totalWidthIncludingSeparators > 0 { // Added check for total > 0
- t.logger.Debug("streamCalculateWidths: Total calculated width (%d incl separators) exceeds global stream width (%d). Shrinking.", totalWidthIncludingSeparators, t.config.Stream.Widths.Global)
+ if t.config.Widths.Global < totalWidthIncludingSeparators && totalWidthIncludingSeparators > 0 { // Added check for total > 0
+ t.logger.Debug("streamCalculateWidths: Total calculated width (%d incl separators) exceeds global stream width (%d). Shrinking.", totalWidthIncludingSeparators, t.config.Widths.Global)
// Target sum for column widths only (global limit - total separator width)
- targetSumForColumnWidths := t.config.Stream.Widths.Global
+ targetSumForColumnWidths := t.config.Widths.Global
if t.streamNumCols > 1 {
targetSumForColumnWidths -= (t.streamNumCols - 1) * separatorWidth
}
@@ -671,7 +671,7 @@ func (t *Table) streamCalculateWidths(sampleDataLines []string, sectionConfigFor
}
t.logger.Debug("streamCalculateWidths: Widths after scaling and distribution: %v", t.streamWidths)
} else {
- t.logger.Debug("streamCalculateWidths: Total calculated width (%d) fits global stream width (%d). No scaling needed.", totalWidthIncludingSeparators, t.config.Stream.Widths.Global)
+ t.logger.Debug("streamCalculateWidths: Total calculated width (%d) fits global stream width (%d). No scaling needed.", totalWidthIncludingSeparators, t.config.Widths.Global)
}
}
diff --git a/vendor/github.com/olekukonko/tablewriter/tablewriter.go b/vendor/github.com/olekukonko/tablewriter/tablewriter.go
index 16961d89a1..4eca55d7a5 100644
--- a/vendor/github.com/olekukonko/tablewriter/tablewriter.go
+++ b/vendor/github.com/olekukonko/tablewriter/tablewriter.go
@@ -133,6 +133,16 @@ func NewTable(w io.Writer, opts ...Option) *Table {
return t
}
+// NewWriter creates a new table with default settings for backward compatibility.
+// It logs the creation if debugging is enabled.
+func NewWriter(w io.Writer) *Table {
+ t := NewTable(w)
+ if t.logger != nil {
+ t.logger.Debug("NewWriter created buffered Table")
+ }
+ return t
+}
+
// Caption sets the table caption (legacy method).
// Defaults to BottomCenter alignment, wrapping to table width.
// Use SetCaptionOptions for more control.
@@ -397,6 +407,11 @@ func (t *Table) Options(opts ...Option) *Table {
t.logger.Suspend()
}
+ // help resolve from deprecation
+ //if t.config.Stream.Enable {
+ // t.config.Widths = t.config.Stream.Widths
+ //}
+
// send logger to renderer
// this will overwrite the default logger
t.renderer.Logger(t.logger)
@@ -508,19 +523,40 @@ func (t *Table) appendSingle(row interface{}) error {
// Parameter config provides alignment settings for the section.
// Returns a map of column indices to alignment settings.
func (t *Table) buildAligns(config tw.CellConfig) map[int]tw.Align {
- t.logger.Debugf("buildAligns INPUT: config.Formatting.Align=%s, config.ColumnAligns=%v", config.Formatting.Alignment, config.ColumnAligns)
+ // Start with global alignment, preferring deprecated Formatting.Alignment
+ effectiveGlobalAlign := config.Formatting.Alignment
+ if effectiveGlobalAlign == tw.Empty || effectiveGlobalAlign == tw.Skip {
+ effectiveGlobalAlign = config.Alignment.Global
+ if config.Formatting.Alignment != tw.Empty && config.Formatting.Alignment != tw.Skip {
+ t.logger.Warnf("Using deprecated CellFormatting.Alignment (%s). Migrate to CellConfig.Alignment.Global.", config.Formatting.Alignment)
+ }
+ }
+
+ // Use per-column alignments, preferring deprecated ColumnAligns
+ effectivePerColumn := config.ColumnAligns
+ if len(effectivePerColumn) == 0 && len(config.Alignment.PerColumn) > 0 {
+ effectivePerColumn = make([]tw.Align, len(config.Alignment.PerColumn))
+ copy(effectivePerColumn, config.Alignment.PerColumn)
+ if len(config.ColumnAligns) > 0 {
+ t.logger.Warnf("Using deprecated CellConfig.ColumnAligns (%v). Migrate to CellConfig.Alignment.PerColumn.", config.ColumnAligns)
+ }
+ }
+
+ // Log input for debugging
+ t.logger.Debugf("buildAligns INPUT: deprecated Formatting.Alignment=%s, deprecated ColumnAligns=%v, config.Alignment.Global=%s, config.Alignment.PerColumn=%v",
+ config.Formatting.Alignment, config.ColumnAligns, config.Alignment.Global, config.Alignment.PerColumn)
+
numColsToUse := t.getNumColsToUse()
colAlignsResult := make(map[int]tw.Align)
for i := 0; i < numColsToUse; i++ {
- currentAlign := config.Formatting.Alignment
- if i < len(config.ColumnAligns) {
- colSpecificAlign := config.ColumnAligns[i]
- if colSpecificAlign != tw.Empty && colSpecificAlign != tw.Skip {
- currentAlign = colSpecificAlign
- }
+ currentAlign := effectiveGlobalAlign
+ if i < len(effectivePerColumn) && effectivePerColumn[i] != tw.Empty && effectivePerColumn[i] != tw.Skip {
+ currentAlign = effectivePerColumn[i]
}
+ // Skip validation here; rely on rendering to handle invalid alignments
colAlignsResult[i] = currentAlign
}
+
t.logger.Debugf("Aligns built: %v (length %d)", colAlignsResult, len(colAlignsResult))
return colAlignsResult
}
@@ -532,7 +568,7 @@ func (t *Table) buildPadding(padding tw.CellPadding) map[int]tw.Padding {
numColsToUse := t.getNumColsToUse()
colPadding := make(map[int]tw.Padding)
for i := 0; i < numColsToUse; i++ {
- if i < len(padding.PerColumn) && padding.PerColumn[i] != (tw.Padding{}) {
+ if i < len(padding.PerColumn) && padding.PerColumn[i].Paddable() {
colPadding[i] = padding.PerColumn[i]
} else {
colPadding[i] = padding.Global
@@ -699,8 +735,12 @@ func (t *Table) maxColumns() int {
return m
}
+// printTopBottomCaption prints the table's caption at the specified top or bottom position.
+// It wraps the caption text to fit the table width or a user-defined width, aligns it according
+// to the specified alignment, and writes it to the provided writer. If the caption text is empty
+// or the spot is invalid, it logs the issue and returns without printing. The function handles
+// wrapping errors by falling back to splitting on newlines or using the original text.
func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
- // Log the state of t.caption
t.logger.Debugf("[printCaption Entry] Text=%q, Spot=%v (type %T), Align=%q, UserWidth=%d, ActualTableWidth=%d",
t.caption.Text, t.caption.Spot, t.caption.Spot, t.caption.Align, t.caption.Width, actualTableWidth)
@@ -711,12 +751,11 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
return
}
- // Determine captionWrapWidth
var captionWrapWidth int
if t.caption.Width > 0 {
captionWrapWidth = t.caption.Width
t.logger.Debugf("[printCaption] Using user-defined caption.Width %d for wrapping.", captionWrapWidth)
- } else if actualTableWidth <= 4 { // Empty or minimal table
+ } else if actualTableWidth <= 4 {
captionWrapWidth = tw.DisplayWidth(t.caption.Text)
t.logger.Debugf("[printCaption] Empty table, no user caption.Width: Using natural caption width %d.", captionWrapWidth)
} else {
@@ -724,14 +763,12 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
t.logger.Debugf("[printCaption] Non-empty table, no user caption.Width: Using actualTableWidth %d for wrapping.", actualTableWidth)
}
- // Ensure captionWrapWidth is positive
if captionWrapWidth <= 0 {
- captionWrapWidth = 10 // Minimum sensible width
+ captionWrapWidth = 10
t.logger.Warnf("[printCaption] captionWrapWidth was %d (<=0). Setting to minimum %d.", captionWrapWidth, 10)
}
t.logger.Debugf("[printCaption] Final captionWrapWidth to be used by twwarp: %d", captionWrapWidth)
- // Wrap the caption text
wrappedCaptionLines, count := twwarp.WrapString(t.caption.Text, captionWrapWidth)
if count == 0 {
t.logger.Errorf("[printCaption] Error from twwarp.WrapString (width %d): %v. Text: %q", captionWrapWidth, count, t.caption.Text)
@@ -754,7 +791,6 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
t.logger.Debugf("[printCaption] Wrapped caption into %d lines: %v", len(wrappedCaptionLines), wrappedCaptionLines)
}
- // Determine padding target width
paddingTargetWidth := actualTableWidth
if t.caption.Width > 0 {
paddingTargetWidth = t.caption.Width
@@ -763,7 +799,6 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
}
t.logger.Debugf("[printCaption] Final paddingTargetWidth for tw.Pad: %d", paddingTargetWidth)
- // Print each wrapped line
for i, line := range wrappedCaptionLines {
align := t.caption.Align
if align == "" || align == tw.AlignDefault || align == tw.AlignNone {
@@ -791,22 +826,11 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
// Parameters include cells to process and config for formatting rules.
// Returns a slice of string slices representing processed lines.
func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string {
- // force max width
-
isStreaming := t.config.Stream.Enable && t.hasPrinted
t.logger.Debugf("prepareContent: Processing cells=%v (streaming: %v)", cells, isStreaming)
initialInputCellCount := len(cells)
result := make([][]string, 0)
- // ll.Dbg(t.config.MaxWidth)
- // force max width
- if t.config.MaxWidth > 0 {
- // it has headers
- if len(cells) > 0 {
- config.ColMaxWidths.Global = int(math.Floor(float64(t.config.MaxWidth) / float64(len(cells))))
- }
- }
-
effectiveNumCols := initialInputCellCount
if isStreaming {
if t.streamNumCols > 0 {
@@ -830,6 +854,16 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string
}
}
+ if t.config.MaxWidth > 0 && !t.config.Widths.Constrained() {
+ if effectiveNumCols > 0 {
+ derivedSectionGlobalMaxWidth := int(math.Floor(float64(t.config.MaxWidth) / float64(effectiveNumCols)))
+ config.ColMaxWidths.Global = derivedSectionGlobalMaxWidth
+ t.logger.Debugf("prepareContent: Table MaxWidth %d active and t.config.Widths not constrained. "+
+ "Derived section ColMaxWidths.Global: %d for %d columns. This will be used by calculateContentMaxWidth if no higher priority constraints exist.",
+ t.config.MaxWidth, config.ColMaxWidths.Global, effectiveNumCols)
+ }
+ }
+
for i := 0; i < effectiveNumCols; i++ {
cellContent := ""
if i < len(cells) {
@@ -841,7 +875,7 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string
cellContent = t.Trimmer(cellContent)
colPad := config.Padding.Global
- if i < len(config.Padding.PerColumn) && config.Padding.PerColumn[i] != (tw.Padding{}) {
+ if i < len(config.Padding.PerColumn) && config.Padding.PerColumn[i].Paddable() {
colPad = config.Padding.PerColumn[i]
}
@@ -881,50 +915,45 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string
case tw.WrapBreak:
wrapped := make([]string, 0)
currentLine := line
+ breakCharWidth := tw.DisplayWidth(tw.CharBreak)
for tw.DisplayWidth(currentLine) > effectiveContentMaxWidth {
- breakPoint := tw.BreakPoint(currentLine, effectiveContentMaxWidth)
- if breakPoint <= 0 {
- t.logger.Warnf("prepareContent: WrapBreak - BreakPoint <= 0 for line '%s' at width %d. Attempting manual break.", currentLine, effectiveContentMaxWidth)
- runes := []rune(currentLine)
+ targetWidth := effectiveContentMaxWidth - breakCharWidth
+ if targetWidth < 0 {
+ targetWidth = 0
+ }
+ breakPoint := tw.BreakPoint(currentLine, targetWidth)
+ runes := []rune(currentLine)
+ if breakPoint <= 0 || breakPoint > len(runes) {
+ t.logger.Warnf("prepareContent: WrapBreak - Invalid BreakPoint %d for line '%s' at width %d. Attempting manual break.", breakPoint, currentLine, targetWidth)
actualBreakRuneCount := 0
tempWidth := 0
- for charIdx, r := range currentLine {
+ for charIdx, r := range runes {
runeStr := string(r)
rw := tw.DisplayWidth(runeStr)
- if tempWidth+rw > effectiveContentMaxWidth && charIdx > 0 {
+ if tempWidth+rw > targetWidth && charIdx > 0 {
break
}
tempWidth += rw
actualBreakRuneCount = charIdx + 1
- if tempWidth >= effectiveContentMaxWidth && charIdx == 0 {
+ if tempWidth >= targetWidth && charIdx == 0 {
break
}
}
if actualBreakRuneCount == 0 && len(runes) > 0 {
actualBreakRuneCount = 1
}
-
if actualBreakRuneCount > 0 && actualBreakRuneCount <= len(runes) {
wrapped = append(wrapped, string(runes[:actualBreakRuneCount])+tw.CharBreak)
currentLine = string(runes[actualBreakRuneCount:])
} else {
- if tw.DisplayWidth(currentLine) > 0 {
- wrapped = append(wrapped, currentLine)
- currentLine = ""
- }
- break
- }
- } else {
- runes := []rune(currentLine)
- if breakPoint <= len(runes) {
- wrapped = append(wrapped, string(runes[:breakPoint])+tw.CharBreak)
- currentLine = string(runes[breakPoint:])
- } else {
- t.logger.Warnf("prepareContent: WrapBreak - BreakPoint (%d) out of bounds for line runes (%d). Adding full line.", breakPoint, len(runes))
+ t.logger.Warnf("prepareContent: WrapBreak - Cannot break line '%s'. Adding as is.", currentLine)
wrapped = append(wrapped, currentLine)
currentLine = ""
break
}
+ } else {
+ wrapped = append(wrapped, string(runes[:breakPoint])+tw.CharBreak)
+ currentLine = string(runes[breakPoint:])
}
}
if tw.DisplayWidth(currentLine) > 0 {
@@ -959,7 +988,8 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string
if i < len(result[j]) {
result[j][i] = cellLineContent
} else {
- t.logger.Warnf("prepareContent: Column index %d out of bounds (%d) during result matrix population.", i, len(result[j]))
+ t.logger.Warnf("prepareContent: Column index %d out of bounds (%d) during result matrix population. EffectiveNumCols: %d. This indicates a logic error.",
+ i, len(result[j]), effectiveNumCols)
}
}
}
@@ -1304,6 +1334,7 @@ func (t *Table) prepareWithMerges(content [][]string, config tw.CellConfig, posi
// No parameters are required.
// Returns an error if rendering fails in any section.
func (t *Table) render() error {
+
t.ensureInitialized()
if t.config.Stream.Enable {
@@ -1339,9 +1370,8 @@ func (t *Table) render() error {
t.logger.Debugf("No caption detected. Rendering table core directly to writer.")
}
- //Render Table Core ---
- t.writer = targetWriter // Set writer only when necessary
-
+ //Render Table Core
+ t.writer = targetWriter
ctx, mctx, err := t.prepareContexts()
if err != nil {
t.writer = originalWriter
@@ -1387,7 +1417,6 @@ func (t *Table) render() error {
if renderError {
return firstRenderErr // Return error from core rendering if any
}
- //End Render Table Core ---
//Caption Handling & Final Output ---
if isTopOrBottomCaption {
@@ -1877,17 +1906,54 @@ func (t *Table) renderHeader(ctx *renderContext, mctx *mergeContext) error {
if cfg.Settings.Lines.ShowHeaderLine.Enabled() && (len(ctx.rowLines) > 0 || len(ctx.footerLines) > 0) {
ctx.logger.Debug("Rendering header separator line")
- hctx.rowIdx = 0
- hctx.lineIdx = len(ctx.headerLines) - 1
- hctx.line = padLine(ctx.headerLines[hctx.lineIdx], ctx.numCols)
- hctx.location = tw.LocationMiddle
resp := t.buildCellContexts(ctx, mctx, hctx, colAligns, colPadding)
+
+ var nextSectionCells map[int]tw.CellContext
+ var nextSectionWidths tw.Mapper[int, int]
+
+ if len(ctx.rowLines) > 0 {
+ nextSectionWidths = ctx.widths[tw.Row]
+ rowColAligns := t.buildAligns(t.config.Row)
+ rowColPadding := t.buildPadding(t.config.Row.Padding)
+ firstRowHctx := &helperContext{
+ position: tw.Row,
+ rowIdx: 0,
+ lineIdx: 0,
+ }
+ if len(ctx.rowLines[0]) > 0 {
+ firstRowHctx.line = padLine(ctx.rowLines[0][0], ctx.numCols)
+ } else {
+ firstRowHctx.line = make([]string, ctx.numCols)
+ }
+ firstRowResp := t.buildCellContexts(ctx, mctx, firstRowHctx, rowColAligns, rowColPadding)
+ nextSectionCells = firstRowResp.cells
+ } else if len(ctx.footerLines) > 0 {
+ nextSectionWidths = ctx.widths[tw.Row]
+ footerColAligns := t.buildAligns(t.config.Footer)
+ footerColPadding := t.buildPadding(t.config.Footer.Padding)
+ firstFooterHctx := &helperContext{
+ position: tw.Footer,
+ rowIdx: 0,
+ lineIdx: 0,
+ }
+ if len(ctx.footerLines) > 0 {
+ firstFooterHctx.line = padLine(ctx.footerLines[0], ctx.numCols)
+ } else {
+ firstFooterHctx.line = make([]string, ctx.numCols)
+ }
+ firstFooterResp := t.buildCellContexts(ctx, mctx, firstFooterHctx, footerColAligns, footerColPadding)
+ nextSectionCells = firstFooterResp.cells
+ } else {
+ nextSectionWidths = ctx.widths[tw.Header]
+ nextSectionCells = nil
+ }
+
f.Line(tw.Formatting{
Row: tw.RowContext{
- Widths: ctx.widths[tw.Header],
+ Widths: nextSectionWidths,
Current: resp.cells,
Previous: resp.prevCells,
- Next: resp.nextCells,
+ Next: nextSectionCells,
Position: tw.Header,
Location: tw.LocationMiddle,
},
diff --git a/vendor/github.com/olekukonko/tablewriter/tw/cell.go b/vendor/github.com/olekukonko/tablewriter/tw/cell.go
index de2fa0057a..0547f01a4c 100644
--- a/vendor/github.com/olekukonko/tablewriter/tw/cell.go
+++ b/vendor/github.com/olekukonko/tablewriter/tw/cell.go
@@ -2,13 +2,17 @@ package tw
// CellFormatting holds formatting options for table cells.
type CellFormatting struct {
- Alignment Align // Text alignment within the cell (e.g., Left, Right, Center)
- AutoWrap int // Wrapping behavior (e.g., WrapTruncate, WrapNormal)
- MergeMode int // Bitmask for merge behavior (e.g., MergeHorizontal, MergeVertical)
+ AutoWrap int // Wrapping behavior (e.g., WrapTruncate, WrapNormal)
+ MergeMode int // Bitmask for merge behavior (e.g., MergeHorizontal, MergeVertical)
// Changed form bool to State
// See https://github.com/olekukonko/tablewriter/issues/261
AutoFormat State // Enables automatic formatting (e.g., title case for headers)
+
+ // Deprecated: kept for compatibility
+ // will be removed soon
+ Alignment Align // Text alignment within the cell (e.g., Left, Right, Center)
+
}
// CellPadding defines padding settings for table cells.
@@ -30,17 +34,31 @@ type CellCallbacks struct {
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)
- ColumnAligns []Align // Per-column alignment overrides
+ Alignment CellAlignment // Alignment configuration for cells
ColMaxWidths CellWidth // Per-column maximum width overrides
+
+ // 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
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/tw/fn.go b/vendor/github.com/olekukonko/tablewriter/tw/fn.go
index 8183e3ea1d..9610b2734b 100644
--- a/vendor/github.com/olekukonko/tablewriter/tw/fn.go
+++ b/vendor/github.com/olekukonko/tablewriter/tw/fn.go
@@ -245,7 +245,7 @@ func IsNumeric(s string) bool {
return err == nil
}
-// SplitCamelCase splits a camelCase or PascalCase string into separate words.
+// 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
@@ -284,10 +284,11 @@ func SplitCamelCase(src string) (entries []string) {
runes[i] = runes[i][:len(runes[i])-1]
}
}
- // Convert rune groups to strings, excluding empty or whitespace-only groups
+ // Convert rune groups to strings, excluding empty, underscore or whitespace-only groups
for _, s := range runes {
- if len(s) > 0 && strings.TrimSpace(string(s)) != "" {
- entries = append(entries, string(s))
+ str := string(s)
+ if len(s) > 0 && strings.TrimSpace(str) != "" && str != "_" {
+ entries = append(entries, str)
}
}
return
diff --git a/vendor/github.com/olekukonko/tablewriter/tw/preset.go b/vendor/github.com/olekukonko/tablewriter/tw/preset.go
new file mode 100644
index 0000000000..acadc25cbe
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/tw/preset.go
@@ -0,0 +1,18 @@
+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}
+)
+
+var (
+
+ // PaddingDefault represents standard single-space padding on left/right
+ // Equivalent to Padding{Left: " ", Right: " ", Overwrite: true}
+ PaddingDefault = Padding{Left: " ", Right: " ", Overwrite: true}
+)
diff --git a/vendor/github.com/olekukonko/tablewriter/tw/renderer.go b/vendor/github.com/olekukonko/tablewriter/tw/renderer.go
index 7078a991cc..6caf05e4eb 100644
--- a/vendor/github.com/olekukonko/tablewriter/tw/renderer.go
+++ b/vendor/github.com/olekukonko/tablewriter/tw/renderer.go
@@ -108,21 +108,18 @@ type Settings struct {
// 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
+ Left State // Left border visibility
+ Right State // Right border visibility
+ Top State // Top border visibility
+ Bottom State // Bottom border visibility
+ Overwrite bool
}
-// BorderNone defines a border configuration with all sides disabled.
-var (
- PaddingNone = Padding{Left: Empty, Right: Empty, Top: Empty, Bottom: Empty}
- 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}
-)
-
type StreamConfig struct {
Enable bool
- Widths CellWidth // Cell/column widths
+
+ // 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
}
diff --git a/vendor/github.com/olekukonko/tablewriter/tw/tw.go b/vendor/github.com/olekukonko/tablewriter/tw/tw.go
index afdfbe3a90..020de742c2 100644
--- a/vendor/github.com/olekukonko/tablewriter/tw/tw.go
+++ b/vendor/github.com/olekukonko/tablewriter/tw/tw.go
@@ -15,6 +15,8 @@ const (
Skip = ""
Space = " "
NewLine = "\n"
+ Column = ":"
+ Dash = "-"
)
// Feature State Constants
diff --git a/vendor/github.com/olekukonko/tablewriter/tw/types.go b/vendor/github.com/olekukonko/tablewriter/tw/types.go
index 1551c67422..29a1862c1d 100644
--- a/vendor/github.com/olekukonko/tablewriter/tw/types.go
+++ b/vendor/github.com/olekukonko/tablewriter/tw/types.go
@@ -132,22 +132,68 @@ func (c Caption) WithWidth(width int) Caption {
return c
}
-// Padding defines custom padding characters for a cell
+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.
+}
+
+// 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 enables trimming of leading and trailing spaces 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
+}
+
+// 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
}
-type Control struct {
- Hide State
+// 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
}
-// Behavior defines table behavior settings that control features like auto-hiding columns and trimming spaces.
-type Behavior struct {
- AutoHide State // Controls whether empty columns are automatically hidden (ignored in streaming mode)
- TrimSpace State // Controls whether leading/trailing spaces are trimmed from cell content
- Header Control
- Footer Control
+// 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
}
diff --git a/vendor/github.com/olekukonko/tablewriter/zoo.go b/vendor/github.com/olekukonko/tablewriter/zoo.go
index a7f63d585d..04a0c15a19 100644
--- a/vendor/github.com/olekukonko/tablewriter/zoo.go
+++ b/vendor/github.com/olekukonko/tablewriter/zoo.go
@@ -6,6 +6,7 @@ import (
"github.com/olekukonko/errors"
"github.com/olekukonko/tablewriter/tw"
"io"
+ "math"
"reflect"
"strconv"
"strings"
@@ -576,26 +577,23 @@ func (t *Table) buildPaddingLineContents(padChar string, widths tw.Mapper[int, i
// Parameter ctx holds rendering state with width maps.
// Returns an error if width calculation fails.
func (t *Table) calculateAndNormalizeWidths(ctx *renderContext) error {
- ctx.logger.Debugf("calculateAndNormalizeWidths: Computing and normalizing widths for %d columns", ctx.numCols)
+ ctx.logger.Debugf("calculateAndNormalizeWidths: Computing and normalizing widths for %d columns. Compact: %v",
+ ctx.numCols, t.config.Behavior.Compact.Merge.Enabled())
// Initialize width maps
- t.headerWidths = tw.NewMapper[int, int]()
- t.rowWidths = tw.NewMapper[int, int]()
- t.footerWidths = tw.NewMapper[int, int]()
+ //t.headerWidths = tw.NewMapper[int, int]()
+ //t.rowWidths = tw.NewMapper[int, int]()
+ //t.footerWidths = tw.NewMapper[int, int]()
- // Compute header widths
+ // Compute content-based widths for each section
for _, lines := range ctx.headerLines {
t.updateWidths(lines, t.headerWidths, t.config.Header.Padding)
}
- ctx.logger.Debugf("Initial Header widths: %v", t.headerWidths)
-
- // Cache row widths to avoid re-iteration
rowWidthCache := make([]tw.Mapper[int, int], len(ctx.rowLines))
for i, row := range ctx.rowLines {
rowWidthCache[i] = tw.NewMapper[int, int]()
for _, line := range row {
t.updateWidths(line, rowWidthCache[i], t.config.Row.Padding)
- // Aggregate into t.rowWidths
for col, width := range rowWidthCache[i] {
currentMax, _ := t.rowWidths.OK(col)
if width > currentMax {
@@ -604,41 +602,348 @@ func (t *Table) calculateAndNormalizeWidths(ctx *renderContext) error {
}
}
}
- ctx.logger.Debugf("Initial Row widths: %v", t.rowWidths)
-
- // Compute footer widths
for _, lines := range ctx.footerLines {
t.updateWidths(lines, t.footerWidths, t.config.Footer.Padding)
}
- ctx.logger.Debugf("Initial Footer widths: %v", t.footerWidths)
-
- // Initialize width maps for normalization
- ctx.widths[tw.Header] = tw.NewMapper[int, int]()
- ctx.widths[tw.Row] = tw.NewMapper[int, int]()
- ctx.widths[tw.Footer] = tw.NewMapper[int, int]()
+ ctx.logger.Debugf("Content-based widths: header=%v, row=%v, footer=%v", t.headerWidths, t.rowWidths, t.footerWidths)
+
+ // Analyze header merges for optimization
+ var headerMergeSpans map[int]int
+ if t.config.Header.Formatting.MergeMode&tw.MergeHorizontal != 0 && len(ctx.headerLines) > 0 {
+ headerMergeSpans = make(map[int]int)
+ visitedCols := make(map[int]bool)
+ firstHeaderLine := ctx.headerLines[0]
+ if len(firstHeaderLine) > 0 {
+ for i := 0; i < len(firstHeaderLine); {
+ if visitedCols[i] {
+ i++
+ continue
+ }
+ var currentLogicalCellContentBuilder strings.Builder
+ for _, hLine := range ctx.headerLines {
+ if i < len(hLine) {
+ currentLogicalCellContentBuilder.WriteString(hLine[i])
+ }
+ }
+ currentHeaderCellContent := t.Trimmer(currentLogicalCellContentBuilder.String())
+ span := 1
+ for j := i + 1; j < len(firstHeaderLine); j++ {
+ var nextLogicalCellContentBuilder strings.Builder
+ for _, hLine := range ctx.headerLines {
+ if j < len(hLine) {
+ nextLogicalCellContentBuilder.WriteString(hLine[j])
+ }
+ }
+ nextHeaderCellContent := t.Trimmer(nextLogicalCellContentBuilder.String())
+ if currentHeaderCellContent == nextHeaderCellContent && currentHeaderCellContent != "" && currentHeaderCellContent != "-" {
+ span++
+ } else {
+ break
+ }
+ }
+ if span > 1 {
+ headerMergeSpans[i] = span
+ for k := 0; k < span; k++ {
+ visitedCols[i+k] = true
+ }
+ }
+ i += span
+ }
+ }
+ if len(headerMergeSpans) > 0 {
+ ctx.logger.Debugf("Header merge spans: %v", headerMergeSpans)
+ }
+ }
- // Normalize widths by taking the maximum across sections
+ // Determine natural column widths
+ naturalColumnWidths := tw.NewMapper[int, int]()
for i := 0; i < ctx.numCols; i++ {
- maxWidth := 0
- for _, w := range []tw.Mapper[int, int]{t.headerWidths, t.rowWidths, t.footerWidths} {
- if wd := w.Get(i); wd > maxWidth {
- maxWidth = wd
+ width := 0
+ if colWidth, ok := t.config.Widths.PerColumn.OK(i); ok && colWidth >= 0 {
+ width = colWidth
+ ctx.logger.Debugf("Col %d width from Config.Widths.PerColumn: %d", i, width)
+ } else {
+ maxRowFooterWidth := tw.Max(t.rowWidths.Get(i), t.footerWidths.Get(i))
+ headerCellOriginalWidth := t.headerWidths.Get(i)
+ if t.config.Behavior.Compact.Merge.Enabled() &&
+ t.config.Header.Formatting.MergeMode&tw.MergeHorizontal != 0 &&
+ headerMergeSpans != nil {
+ isColInHeaderMerge := false
+ for startCol, span := range headerMergeSpans {
+ if i >= startCol && i < startCol+span {
+ isColInHeaderMerge = true
+ break
+ }
+ }
+ if isColInHeaderMerge {
+ width = maxRowFooterWidth
+ if width == 0 && headerCellOriginalWidth > 0 {
+ width = headerCellOriginalWidth
+ }
+ ctx.logger.Debugf("Col %d (in merge) width: %d (row/footer: %d, header: %d)", i, width, maxRowFooterWidth, headerCellOriginalWidth)
+ } else {
+ width = tw.Max(headerCellOriginalWidth, maxRowFooterWidth)
+ ctx.logger.Debugf("Col %d (not in merge) width: %d", i, width)
+ }
+ } else {
+ width = tw.Max(tw.Max(headerCellOriginalWidth, t.rowWidths.Get(i)), t.footerWidths.Get(i))
+ ctx.logger.Debugf("Col %d width (no merge): %d", i, width)
+ }
+ if width == 0 && (headerCellOriginalWidth > 0 || t.rowWidths.Get(i) > 0 || t.footerWidths.Get(i) > 0) {
+ width = tw.Max(tw.Max(headerCellOriginalWidth, t.rowWidths.Get(i)), t.footerWidths.Get(i))
+ }
+ if width == 0 {
+ width = 1
+ }
+ }
+ naturalColumnWidths.Set(i, width)
+ }
+ ctx.logger.Debugf("Natural column widths: %v", naturalColumnWidths)
+
+ // Expand columns for merged header content if needed
+ workingWidths := naturalColumnWidths.Clone()
+ if t.config.Header.Formatting.MergeMode&tw.MergeHorizontal != 0 && headerMergeSpans != nil {
+ if span, isOneBigMerge := headerMergeSpans[0]; isOneBigMerge && span == ctx.numCols && ctx.numCols > 0 {
+ var firstHeaderCellLogicalContentBuilder strings.Builder
+ for _, hLine := range ctx.headerLines {
+ if 0 < len(hLine) {
+ firstHeaderCellLogicalContentBuilder.WriteString(hLine[0])
+ }
+ }
+ mergedContentString := t.Trimmer(firstHeaderCellLogicalContentBuilder.String())
+ headerCellPadding := t.config.Header.Padding.Global
+ if 0 < len(t.config.Header.Padding.PerColumn) && t.config.Header.Padding.PerColumn[0].Paddable() {
+ headerCellPadding = t.config.Header.Padding.PerColumn[0]
+ }
+ actualMergedHeaderContentPhysicalWidth := tw.DisplayWidth(mergedContentString) +
+ tw.DisplayWidth(headerCellPadding.Left) +
+ tw.DisplayWidth(headerCellPadding.Right)
+ currentSumOfColumnWidths := 0
+ workingWidths.Each(func(_ int, w int) { currentSumOfColumnWidths += w })
+ numSeparatorsInFullSpan := 0
+ if ctx.numCols > 1 {
+ if t.renderer != nil && t.renderer.Config().Settings.Separators.BetweenColumns.Enabled() {
+ numSeparatorsInFullSpan = (ctx.numCols - 1) * tw.DisplayWidth(t.renderer.Config().Symbols.Column())
+ }
+ }
+ totalCurrentSpanPhysicalWidth := currentSumOfColumnWidths + numSeparatorsInFullSpan
+ if actualMergedHeaderContentPhysicalWidth > totalCurrentSpanPhysicalWidth {
+ ctx.logger.Debugf("Merged header content '%s' (width %d) exceeds total width %d. Expanding.",
+ mergedContentString, actualMergedHeaderContentPhysicalWidth, totalCurrentSpanPhysicalWidth)
+ shortfall := actualMergedHeaderContentPhysicalWidth - totalCurrentSpanPhysicalWidth
+ numNonZeroCols := 0
+ workingWidths.Each(func(_ int, w int) {
+ if w > 0 {
+ numNonZeroCols++
+ }
+ })
+ if numNonZeroCols == 0 && ctx.numCols > 0 {
+ numNonZeroCols = ctx.numCols
+ }
+ if numNonZeroCols > 0 && shortfall > 0 {
+ extraPerColumn := int(math.Ceil(float64(shortfall) / float64(numNonZeroCols)))
+ finalSumAfterExpansion := 0
+ workingWidths.Each(func(colIdx int, currentW int) {
+ if currentW > 0 || (numNonZeroCols == ctx.numCols && ctx.numCols > 0) {
+ newWidth := currentW + extraPerColumn
+ workingWidths.Set(colIdx, newWidth)
+ finalSumAfterExpansion += newWidth
+ ctx.logger.Debugf("Col %d expanded by %d to %d", colIdx, extraPerColumn, newWidth)
+ } else {
+ finalSumAfterExpansion += currentW
+ }
+ })
+ overDistributed := (finalSumAfterExpansion + numSeparatorsInFullSpan) - actualMergedHeaderContentPhysicalWidth
+ if overDistributed > 0 {
+ ctx.logger.Debugf("Correcting over-distribution of %d", overDistributed)
+ // Sort columns for deterministic reduction
+ sortedCols := workingWidths.SortedKeys()
+ for i := 0; i < overDistributed; i++ {
+ // Reduce from highest-indexed column
+ for j := len(sortedCols) - 1; j >= 0; j-- {
+ col := sortedCols[j]
+ if workingWidths.Get(col) > 1 && naturalColumnWidths.Get(col) < workingWidths.Get(col) {
+ workingWidths.Set(col, workingWidths.Get(col)-1)
+ ctx.logger.Debugf("Reduced col %d by 1 to %d", col, workingWidths.Get(col))
+ break
+ }
+ }
+ }
+ }
+ }
}
}
- ctx.widths[tw.Header].Set(i, maxWidth)
- ctx.widths[tw.Row].Set(i, maxWidth)
- ctx.widths[tw.Footer].Set(i, maxWidth)
}
- ctx.logger.Debugf("Normalized widths: header=%v, row=%v, footer=%v", ctx.widths[tw.Header], ctx.widths[tw.Row], ctx.widths[tw.Footer])
+ ctx.logger.Debugf("Widths after merged header expansion: %v", workingWidths)
+
+ // Apply global width constraint
+ finalWidths := workingWidths.Clone()
+ if t.config.Widths.Global > 0 {
+ ctx.logger.Debugf("Applying global width constraint: %d", t.config.Widths.Global)
+ currentSumOfFinalColWidths := 0
+ finalWidths.Each(func(_ int, w int) { currentSumOfFinalColWidths += w })
+ numSeparators := 0
+ if ctx.numCols > 1 && t.renderer != nil && t.renderer.Config().Settings.Separators.BetweenColumns.Enabled() {
+ numSeparators = (ctx.numCols - 1) * tw.DisplayWidth(t.renderer.Config().Symbols.Column())
+ }
+ totalCurrentTablePhysicalWidth := currentSumOfFinalColWidths + numSeparators
+ if totalCurrentTablePhysicalWidth > t.config.Widths.Global {
+ ctx.logger.Debugf("Table width %d exceeds global limit %d. Shrinking.", totalCurrentTablePhysicalWidth, t.config.Widths.Global)
+ targetTotalColumnContentWidth := t.config.Widths.Global - numSeparators
+ if targetTotalColumnContentWidth < 0 {
+ targetTotalColumnContentWidth = 0
+ }
+ if ctx.numCols > 0 && targetTotalColumnContentWidth < ctx.numCols {
+ targetTotalColumnContentWidth = ctx.numCols
+ }
+ hardMinimums := tw.NewMapper[int, int]()
+ sumOfHardMinimums := 0
+ isHeaderContentHardToWrap := !(t.config.Header.Formatting.AutoWrap == tw.WrapNormal || t.config.Header.Formatting.AutoWrap == tw.WrapBreak)
+ for i := 0; i < ctx.numCols; i++ {
+ minW := 1
+ if isHeaderContentHardToWrap && len(ctx.headerLines) > 0 {
+ headerColNaturalWidthWithPadding := t.headerWidths.Get(i)
+ if headerColNaturalWidthWithPadding > minW {
+ minW = headerColNaturalWidthWithPadding
+ }
+ }
+ hardMinimums.Set(i, minW)
+ sumOfHardMinimums += minW
+ }
+ ctx.logger.Debugf("Hard minimums: %v (sum: %d)", hardMinimums, sumOfHardMinimums)
+ if targetTotalColumnContentWidth < sumOfHardMinimums && sumOfHardMinimums > 0 {
+ ctx.logger.Warnf("Target width %d below minimums %d. Scaling.", targetTotalColumnContentWidth, sumOfHardMinimums)
+ scaleFactorMin := float64(targetTotalColumnContentWidth) / float64(sumOfHardMinimums)
+ if scaleFactorMin < 0 {
+ scaleFactorMin = 0
+ }
+ tempSum := 0
+ scaledHardMinimums := tw.NewMapper[int, int]()
+ hardMinimums.Each(func(colIdx int, currentMinW int) {
+ scaledMinW := int(math.Round(float64(currentMinW) * scaleFactorMin))
+ if scaledMinW < 1 && targetTotalColumnContentWidth > 0 {
+ scaledMinW = 1
+ } else if scaledMinW < 0 {
+ scaledMinW = 0
+ }
+ scaledHardMinimums.Set(colIdx, scaledMinW)
+ tempSum += scaledMinW
+ })
+ errorDiffMin := targetTotalColumnContentWidth - tempSum
+ if errorDiffMin != 0 && scaledHardMinimums.Len() > 0 {
+ sortedKeys := scaledHardMinimums.SortedKeys()
+ for i := 0; i < int(math.Abs(float64(errorDiffMin))); i++ {
+ keyToAdjust := sortedKeys[i%len(sortedKeys)]
+ val := scaledHardMinimums.Get(keyToAdjust)
+ adj := 1
+ if errorDiffMin < 0 {
+ adj = -1
+ }
+ if val+adj >= 1 || (val+adj == 0 && targetTotalColumnContentWidth == 0) {
+ scaledHardMinimums.Set(keyToAdjust, val+adj)
+ } else if adj > 0 {
+ scaledHardMinimums.Set(keyToAdjust, val+adj)
+ }
+ }
+ }
+ finalWidths = scaledHardMinimums.Clone()
+ ctx.logger.Debugf("Scaled minimums: %v", finalWidths)
+ } else {
+ finalWidths = hardMinimums.Clone()
+ widthAllocatedByMinimums := sumOfHardMinimums
+ remainingWidthToDistribute := targetTotalColumnContentWidth - widthAllocatedByMinimums
+ ctx.logger.Debugf("Target: %d, minimums: %d, remaining: %d", targetTotalColumnContentWidth, widthAllocatedByMinimums, remainingWidthToDistribute)
+ if remainingWidthToDistribute > 0 {
+ sumOfFlexiblePotentialBase := 0
+ flexibleColsOriginalWidths := tw.NewMapper[int, int]()
+ for i := 0; i < ctx.numCols; i++ {
+ naturalW := workingWidths.Get(i)
+ minW := hardMinimums.Get(i)
+ if naturalW > minW {
+ sumOfFlexiblePotentialBase += (naturalW - minW)
+ flexibleColsOriginalWidths.Set(i, naturalW)
+ }
+ }
+ ctx.logger.Debugf("Flexible potential: %d, flexible widths: %v", sumOfFlexiblePotentialBase, flexibleColsOriginalWidths)
+ if sumOfFlexiblePotentialBase > 0 {
+ distributedExtraSum := 0
+ sortedFlexKeys := flexibleColsOriginalWidths.SortedKeys()
+ for _, colIdx := range sortedFlexKeys {
+ naturalWOfCol := flexibleColsOriginalWidths.Get(colIdx)
+ hardMinOfCol := hardMinimums.Get(colIdx)
+ flexiblePartOfCol := naturalWOfCol - hardMinOfCol
+ proportion := 0.0
+ if sumOfFlexiblePotentialBase > 0 {
+ proportion = float64(flexiblePartOfCol) / float64(sumOfFlexiblePotentialBase)
+ } else if len(sortedFlexKeys) > 0 {
+ proportion = 1.0 / float64(len(sortedFlexKeys))
+ }
+ extraForThisCol := int(math.Round(float64(remainingWidthToDistribute) * proportion))
+ currentAssignedW := finalWidths.Get(colIdx)
+ finalWidths.Set(colIdx, currentAssignedW+extraForThisCol)
+ distributedExtraSum += extraForThisCol
+ }
+ errorInDist := remainingWidthToDistribute - distributedExtraSum
+ ctx.logger.Debugf("Distributed %d, error: %d", distributedExtraSum, errorInDist)
+ if errorInDist != 0 && len(sortedFlexKeys) > 0 {
+ for i := 0; i < int(math.Abs(float64(errorInDist))); i++ {
+ colToAdjust := sortedFlexKeys[i%len(sortedFlexKeys)]
+ w := finalWidths.Get(colToAdjust)
+ adj := 1
+ if errorInDist < 0 {
+ adj = -1
+ }
+ if !(adj < 0 && w+adj < hardMinimums.Get(colToAdjust)) {
+ finalWidths.Set(colToAdjust, w+adj)
+ } else if adj > 0 {
+ finalWidths.Set(colToAdjust, w+adj)
+ }
+ }
+ }
+ } else {
+ if ctx.numCols > 0 {
+ extraPerCol := remainingWidthToDistribute / ctx.numCols
+ rem := remainingWidthToDistribute % ctx.numCols
+ for i := 0; i < ctx.numCols; i++ {
+ currentW := finalWidths.Get(i)
+ add := extraPerCol
+ if i < rem {
+ add++
+ }
+ finalWidths.Set(i, currentW+add)
+ }
+ }
+ }
+ }
+ }
+ finalSumCheck := 0
+ finalWidths.Each(func(idx int, w int) {
+ if w < 1 && targetTotalColumnContentWidth > 0 {
+ finalWidths.Set(idx, 1)
+ } else if w < 0 {
+ finalWidths.Set(idx, 0)
+ }
+ finalSumCheck += finalWidths.Get(idx)
+ })
+ ctx.logger.Debugf("Final widths after scaling: %v (sum: %d, target: %d)", finalWidths, finalSumCheck, targetTotalColumnContentWidth)
+ }
+ }
+
+ // Assign final widths to context
+ ctx.widths[tw.Header] = finalWidths.Clone()
+ ctx.widths[tw.Row] = finalWidths.Clone()
+ ctx.widths[tw.Footer] = finalWidths.Clone()
+ ctx.logger.Debugf("Final normalized widths: header=%v, row=%v, footer=%v", ctx.widths[tw.Header], ctx.widths[tw.Row], ctx.widths[tw.Footer])
return nil
}
// calculateContentMaxWidth computes the maximum content width for a column, accounting for padding and mode-specific constraints.
// Returns the effective content width (after subtracting padding) for the given column index.
func (t *Table) calculateContentMaxWidth(colIdx int, config tw.CellConfig, padLeftWidth, padRightWidth int, isStreaming bool) int {
-
var effectiveContentMaxWidth int
+
if isStreaming {
+ // Existing streaming logic remains unchanged
totalColumnWidthFromStream := t.streamWidths.Get(colIdx)
if totalColumnWidthFromStream < 0 {
totalColumnWidthFromStream = 0
@@ -652,28 +957,57 @@ func (t *Table) calculateContentMaxWidth(colIdx int, config tw.CellConfig, padLe
if totalColumnWidthFromStream == 0 {
effectiveContentMaxWidth = 0
}
- t.logger.Debugf("calculateContentMaxWidth: Streaming col %d, TotalColWd=%d, PadL=%d, PadR=%d -> ContentMaxWd=%d",
- colIdx, totalColumnWidthFromStream, padLeftWidth, padRightWidth, effectiveContentMaxWidth)
+ t.logger.Debugf("calculateContentMaxWidth: Streaming col %d, TotalColWd=%d, PadL=%d, PadR=%d -> ContentMaxWd=%d", colIdx, totalColumnWidthFromStream, padLeftWidth, padRightWidth, effectiveContentMaxWidth)
} else {
- hasConstraint := false
+ // New priority-based width constraint checking
constraintTotalCellWidth := 0
- if config.ColMaxWidths.PerColumn != nil {
+ hasConstraint := false
+
+ // 1. Check new Widths.PerColumn (highest priority)
+ if t.config.Widths.Constrained() {
+
+ if colWidth, ok := t.config.Widths.PerColumn.OK(colIdx); ok && colWidth > 0 {
+ constraintTotalCellWidth = colWidth
+ hasConstraint = true
+ t.logger.Debugf("calculateContentMaxWidth: Using Widths.PerColumn[%d] = %d",
+ colIdx, constraintTotalCellWidth)
+ }
+
+ // 2. Check new Widths.Global
+ if !hasConstraint && t.config.Widths.Global > 0 {
+ constraintTotalCellWidth = t.config.Widths.Global
+ hasConstraint = true
+ t.logger.Debugf("calculateContentMaxWidth: Using Widths.Global = %d", constraintTotalCellWidth)
+ }
+ }
+
+ // 3. Fall back to legacy ColMaxWidths.PerColumn (backward compatibility)
+ if !hasConstraint && config.ColMaxWidths.PerColumn != nil {
if colMax, ok := config.ColMaxWidths.PerColumn.OK(colIdx); ok && colMax > 0 {
constraintTotalCellWidth = colMax
hasConstraint = true
- t.logger.Debugf("calculateContentMaxWidth: Batch col %d using config.ColMaxWidths.PerColumn (as total cell width constraint): %d", colIdx, constraintTotalCellWidth)
+ t.logger.Debugf("calculateContentMaxWidth: Using legacy ColMaxWidths.PerColumn[%d] = %d",
+ colIdx, constraintTotalCellWidth)
}
}
+
+ // 4. Fall back to legacy ColMaxWidths.Global
if !hasConstraint && config.ColMaxWidths.Global > 0 {
constraintTotalCellWidth = config.ColMaxWidths.Global
hasConstraint = true
- t.logger.Debugf("calculateContentMaxWidth: Batch col %d using config.Formatting.MaxWidth (as total cell width constraint): %d", colIdx, constraintTotalCellWidth)
+ t.logger.Debugf("calculateContentMaxWidth: Using legacy ColMaxWidths.Global = %d",
+ constraintTotalCellWidth)
}
+
+ // 5. Fall back to table MaxWidth if auto-wrapping
if !hasConstraint && t.config.MaxWidth > 0 && config.Formatting.AutoWrap != tw.WrapNone {
constraintTotalCellWidth = t.config.MaxWidth
hasConstraint = true
- t.logger.Debugf("calculateContentMaxWidth: Batch col %d using t.config.MaxWidth (as total cell width constraint, due to AutoWrap != WrapNone): %d", colIdx, constraintTotalCellWidth)
+ t.logger.Debugf("calculateContentMaxWidth: Using table MaxWidth = %d (AutoWrap enabled)",
+ constraintTotalCellWidth)
}
+
+ // Calculate effective width based on found constraint
if hasConstraint {
effectiveContentMaxWidth = constraintTotalCellWidth - padLeftWidth - padRightWidth
if effectiveContentMaxWidth < 1 && constraintTotalCellWidth > (padLeftWidth+padRightWidth) {
@@ -681,13 +1015,14 @@ func (t *Table) calculateContentMaxWidth(colIdx int, config tw.CellConfig, padLe
} else if effectiveContentMaxWidth < 0 {
effectiveContentMaxWidth = 0
}
- t.logger.Debugf("calculateContentMaxWidth: Batch col %d, ConstraintTotalCellWidth=%d, PadL=%d, PadR=%d -> EffectiveContentMaxWidth=%d",
- colIdx, constraintTotalCellWidth, padLeftWidth, padRightWidth, effectiveContentMaxWidth)
+ t.logger.Debugf("calculateContentMaxWidth: ConstraintTotalCellWidth=%d, PadL=%d, PadR=%d -> EffectiveContentMaxWidth=%d",
+ constraintTotalCellWidth, padLeftWidth, padRightWidth, effectiveContentMaxWidth)
} else {
effectiveContentMaxWidth = 0
- t.logger.Debugf("calculateContentMaxWidth: Batch col %d, No applicable MaxWidth constraint. EffectiveContentMaxWidth set to 0 (unlimited for this stage).", colIdx)
+ t.logger.Debugf("calculateContentMaxWidth: No width constraints found for column %d", colIdx)
}
}
+
return effectiveContentMaxWidth
}
@@ -698,6 +1033,8 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
return nil, errors.New("internal error: convertToStringer called with nil t.stringer")
}
+ t.logger.Debugf("convertToString attempt %v using %v", input, t.stringer)
+
inputType := reflect.TypeOf(input)
stringerFuncVal := reflect.ValueOf(t.stringer)
stringerFuncType := stringerFuncVal.Type()
@@ -1310,7 +1647,8 @@ func (t *Table) updateWidths(row []string, widths tw.Mapper[int, int], padding t
t.logger.Debugf("Updating widths for row: %v", row)
for i, cell := range row {
colPad := padding.Global
- if i < len(padding.PerColumn) && padding.PerColumn[i] != (tw.Padding{}) {
+
+ if i < len(padding.PerColumn) && padding.PerColumn[i].Paddable() {
colPad = padding.PerColumn[i]
t.logger.Debugf(" Col %d: Using per-column padding: L:'%s' R:'%s'", i, colPad.Left, colPad.Right)
} else {
diff --git a/vendor/github.com/pion/interceptor/.golangci.yml b/vendor/github.com/pion/interceptor/.golangci.yml
index a3235bec28..120faf29be 100644
--- a/vendor/github.com/pion/interceptor/.golangci.yml
+++ b/vendor/github.com/pion/interceptor/.golangci.yml
@@ -19,23 +19,42 @@ linters-settings:
recommendations:
- errors
forbidigo:
+ analyze-types: true
forbid:
- ^fmt.Print(f|ln)?$
- ^log.(Panic|Fatal|Print)(f|ln)?$
- ^os.Exit$
- ^panic$
- ^print(ln)?$
+ - p: ^testing.T.(Error|Errorf|Fatal|Fatalf|Fail|FailNow)$
+ pkg: ^testing$
+ msg: "use testify/assert instead"
+ varnamelen:
+ max-distance: 12
+ min-name-length: 2
+ ignore-type-assert-ok: true
+ ignore-map-index-ok: true
+ ignore-chan-recv-ok: true
+ ignore-decls:
+ - i int
+ - n int
+ - w io.Writer
+ - r io.Reader
+ - b []byte
linters:
enable:
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
- bidichk # Checks for dangerous unicode character sequences
- bodyclose # checks whether HTTP response body is closed successfully
+ - containedctx # containedctx is a linter that detects struct contained context.Context field
- contextcheck # check the function whether use a non-inherited context
+ - cyclop # checks function and package cyclomatic complexity
- decorder # check declaration order and count of types, constants, variables and functions
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
- dupl # Tool for code clone detection
- durationcheck # check for two durations multiplied together
+ - err113 # Golang linter to check the errors handling expressions
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
@@ -46,18 +65,17 @@ linters:
- forcetypeassert # finds forced type assertions
- gci # Gci control golang package import order and make it always deterministic.
- gochecknoglobals # Checks that no globals are present in Go code
- - gochecknoinits # Checks that no init functions are present in Go code
- gocognit # Computes and checks the cognitive complexity of functions
- goconst # Finds repeated strings that could be replaced by a constant
- gocritic # The most opinionated Go source code linter
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
- godox # Tool for detection of FIXME, TODO and other comment keywords
- - err113 # Golang linter to check the errors handling expressions
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
- gofumpt # Gofumpt checks whether code was gofumpt-ed.
- goheader # Checks is file header matches to pattern
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
- - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
- goprintffuncname # Checks that printf-like functions are named with `f` at the end
- gosec # Inspects source code for security problems
- gosimple # Linter for Go source code that specializes in simplifying a code
@@ -65,9 +83,15 @@ linters:
- grouper # An analyzer to analyze expression groups.
- importas # Enforces consistent import aliases
- ineffassign # Detects when assignments to existing variables are not used
+ - lll # Reports long lines
+ - maintidx # maintidx measures the maintainability index of each function.
+ - makezero # Finds slice declarations with non-zero initial length
- misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - nestif # Reports deeply nested if statements
- nilerr # Finds the code that returns nil even if it checks that the error is not nil.
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
- noctx # noctx finds sending http request without context.Context
- predeclared # find code that shadows one of Go's predeclared identifiers
- revive # golint replacement, finds style mistakes
@@ -75,28 +99,22 @@ linters:
- stylecheck # Stylecheck is a replacement for golint
- tagliatelle # Checks the struct tags.
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
- - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
+ - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
- unconvert # Remove unnecessary type conversions
- unparam # Reports unused function parameters
- unused # Checks Go code for unused constants, variables, functions and types
+ - varnamelen # checks that the length of a variable's name matches its scope
- wastedassign # wastedassign finds wasted assignment statements
- whitespace # Tool for detection of leading and trailing whitespace
disable:
- depguard # Go linter that checks if package imports are in a list of acceptable packages
- - containedctx # containedctx is a linter that detects struct contained context.Context field
- - cyclop # checks function and package cyclomatic complexity
- funlen # Tool for detection of long functions
- - gocyclo # Computes and checks the cyclomatic complexity of functions
- - godot # Check if comments end in a period
- - gomnd # An analyzer to detect magic numbers.
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - interfacebloat # A linter that checks length of interface.
- ireturn # Accept Interfaces, Return Concrete Types
- - lll # Reports long lines
- - maintidx # maintidx measures the maintainability index of each function.
- - makezero # Finds slice declarations with non-zero initial length
- - nakedret # Finds naked returns in functions greater than a specified function length
- - nestif # Reports deeply nested if statements
- - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - mnd # An analyzer to detect magic numbers
- nolintlint # Reports ill-formed or insufficient nolint directives
- paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
- prealloc # Finds slice declarations that could potentially be preallocated
@@ -104,8 +122,7 @@ linters:
- rowserrcheck # checks whether Err of rows is checked successfully
- sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
- testpackage # linter that makes you use a separate _test package
- - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
- - varnamelen # checks that the length of a variable's name matches its scope
+ - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
- wrapcheck # Checks that errors returned from external packages are wrapped
- wsl # Whitespace Linter - Forces you to use empty lines!
@@ -114,9 +131,12 @@ issues:
exclude-dirs-use-default: false
exclude-rules:
# Allow complex tests and examples, better to be self contained
- - path: (examples|main\.go|_test\.go)
+ - path: (examples|main\.go)
linters:
+ - gocognit
- forbidigo
+ - path: _test\.go
+ linters:
- gocognit
# Allow forbidden identifiers in CLI commands
diff --git a/vendor/github.com/pion/interceptor/README.md b/vendor/github.com/pion/interceptor/README.md
index 5db5d44143..46a9ca6a2d 100644
--- a/vendor/github.com/pion/interceptor/README.md
+++ b/vendor/github.com/pion/interceptor/README.md
@@ -3,10 +3,10 @@
Pion Interceptor
-RTCP and RTCP processors for building real time communications
+RTP and RTCP processors for building real time communications
-
+
@@ -36,12 +36,12 @@ by anyone. With the following tenets in mind.
* [Google Congestion Control](https://github.com/pion/interceptor/tree/master/pkg/gcc)
* [Stats](https://github.com/pion/interceptor/tree/master/pkg/stats) A [webrtc-stats](https://www.w3.org/TR/webrtc-stats/) compliant statistics generation
* [Interval PLI](https://github.com/pion/interceptor/tree/master/pkg/intervalpli) Generate PLI on a interval. Useful when no decoder is available.
+* [FlexFec](https://github.com/pion/interceptor/tree/master/pkg/flexfec) – [FlexFEC-03](https://datatracker.ietf.org/doc/html/draft-ietf-payload-flexible-fec-scheme-03) encoder implementation
### Planned Interceptors
* Bandwidth Estimation
- [NADA](https://tools.ietf.org/html/rfc8698)
* JitterBuffer, re-order packets and wait for arrival
-* [FlexFec](https://tools.ietf.org/html/draft-ietf-payload-flexible-fec-scheme-20)
* [RTCP Feedback for Congestion Control](https://datatracker.ietf.org/doc/html/rfc8888) the standardized alternative to TWCC.
### Interceptor Public API
@@ -70,9 +70,9 @@ You should also look in [pion/webrtc](https://github.com/pion/webrtc) for real w
The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
### Community
-Pion has an active community on the [Slack](https://pion.ly/slack).
+Pion has an active community on the [Discord](https://discord.gg/PngbdqpFbt).
-Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
+Follow the [Pion Bluesky](https://bsky.app/profile/pion.ly) or [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
We are always looking to support **your projects**. Please reach out if you have something to build!
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
diff --git a/vendor/github.com/pion/interceptor/attributes.go b/vendor/github.com/pion/interceptor/attributes.go
index 8b6d0f5cf6..e4257b8da2 100644
--- a/vendor/github.com/pion/interceptor/attributes.go
+++ b/vendor/github.com/pion/interceptor/attributes.go
@@ -19,7 +19,7 @@ const (
var errInvalidType = errors.New("found value of invalid type in attributes map")
-// Attributes are a generic key/value store used by interceptors
+// Attributes are a generic key/value store used by interceptors.
type Attributes map[interface{}]interface{}
// Get returns the attribute associated with key.
@@ -39,6 +39,7 @@ func (a Attributes) GetRTPHeader(raw []byte) (*rtp.Header, error) {
if header, ok := val.(*rtp.Header); ok {
return header, nil
}
+
return nil, errInvalidType
}
header := &rtp.Header{}
@@ -46,6 +47,7 @@ func (a Attributes) GetRTPHeader(raw []byte) (*rtp.Header, error) {
return nil, err
}
a[rtpHeaderKey] = header
+
return header, nil
}
@@ -57,6 +59,7 @@ func (a Attributes) GetRTCPPackets(raw []byte) ([]rtcp.Packet, error) {
if packets, ok := val.([]rtcp.Packet); ok {
return packets, nil
}
+
return nil, errInvalidType
}
pkts, err := rtcp.Unmarshal(raw)
@@ -64,5 +67,6 @@ func (a Attributes) GetRTCPPackets(raw []byte) ([]rtcp.Packet, error) {
return nil, err
}
a[rtcpPacketsKey] = pkts
+
return pkts, nil
}
diff --git a/vendor/github.com/pion/interceptor/chain.go b/vendor/github.com/pion/interceptor/chain.go
index 267f366510..62c0aeb12e 100644
--- a/vendor/github.com/pion/interceptor/chain.go
+++ b/vendor/github.com/pion/interceptor/chain.go
@@ -50,7 +50,8 @@ func (i *Chain) UnbindLocalStream(ctx *StreamInfo) {
}
}
-// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
+// BindRemoteStream lets you modify any incoming RTP packets.
+// It is called once for per RemoteStream. The returned method
// will be called once per rtp packet.
func (i *Chain) BindRemoteStream(ctx *StreamInfo, reader RTPReader) RTPReader {
for _, interceptor := range i.interceptors {
diff --git a/vendor/github.com/pion/interceptor/errors.go b/vendor/github.com/pion/interceptor/errors.go
index 3dafee3ef0..02c79539c8 100644
--- a/vendor/github.com/pion/interceptor/errors.go
+++ b/vendor/github.com/pion/interceptor/errors.go
@@ -18,6 +18,7 @@ func flattenErrs(errs []error) error {
if len(errs2) == 0 {
return nil
}
+
return multiError(errs2)
}
@@ -50,5 +51,6 @@ func (me multiError) Is(err error) bool {
}
}
}
+
return false
}
diff --git a/vendor/github.com/pion/interceptor/interceptor.go b/vendor/github.com/pion/interceptor/interceptor.go
index c6ba53242e..10d3e32fca 100644
--- a/vendor/github.com/pion/interceptor/interceptor.go
+++ b/vendor/github.com/pion/interceptor/interceptor.go
@@ -12,7 +12,7 @@ import (
"github.com/pion/rtp"
)
-// Factory provides an interface for constructing interceptors
+// Factory provides an interface for constructing interceptors.
type Factory interface {
NewInterceptor(id string) (Interceptor, error)
}
@@ -35,7 +35,8 @@ type Interceptor interface {
// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
UnbindLocalStream(info *StreamInfo)
- // BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
+ // BindRemoteStream lets you modify any incoming RTP packets.
+ // It is called once for per RemoteStream. The returned method
// will be called once per rtp packet.
BindRemoteStream(info *StreamInfo, reader RTPReader) RTPReader
@@ -69,34 +70,34 @@ type RTCPReader interface {
Read([]byte, Attributes) (int, Attributes, error)
}
-// RTPWriterFunc is an adapter for RTPWrite interface
+// RTPWriterFunc is an adapter for RTPWrite interface.
type RTPWriterFunc func(header *rtp.Header, payload []byte, attributes Attributes) (int, error)
-// RTPReaderFunc is an adapter for RTPReader interface
+// RTPReaderFunc is an adapter for RTPReader interface.
type RTPReaderFunc func([]byte, Attributes) (int, Attributes, error)
-// RTCPWriterFunc is an adapter for RTCPWriter interface
+// RTCPWriterFunc is an adapter for RTCPWriter interface.
type RTCPWriterFunc func(pkts []rtcp.Packet, attributes Attributes) (int, error)
-// RTCPReaderFunc is an adapter for RTCPReader interface
+// RTCPReaderFunc is an adapter for RTCPReader interface.
type RTCPReaderFunc func([]byte, Attributes) (int, Attributes, error)
-// Write a rtp packet
+// Write a rtp packet.
func (f RTPWriterFunc) Write(header *rtp.Header, payload []byte, attributes Attributes) (int, error) {
return f(header, payload, attributes)
}
-// Read a rtp packet
+// Read a rtp packet.
func (f RTPReaderFunc) Read(b []byte, a Attributes) (int, Attributes, error) {
return f(b, a)
}
-// Write a batch of rtcp packets
+// Write a batch of rtcp packets.
func (f RTCPWriterFunc) Write(pkts []rtcp.Packet, attributes Attributes) (int, error) {
return f(pkts, attributes)
}
-// Read a batch of rtcp packets
+// Read a batch of rtcp packets.
func (f RTCPReaderFunc) Read(b []byte, a Attributes) (int, Attributes, error) {
return f(b, a)
}
diff --git a/vendor/github.com/pion/interceptor/internal/ntp/ntp.go b/vendor/github.com/pion/interceptor/internal/ntp/ntp.go
index 69f98d6c98..266f13d887 100644
--- a/vendor/github.com/pion/interceptor/internal/ntp/ntp.go
+++ b/vendor/github.com/pion/interceptor/internal/ntp/ntp.go
@@ -9,7 +9,7 @@ import (
"time"
)
-// ToNTP converts a time.Time oboject to an uint64 NTP timestamp
+// ToNTP converts a time.Time oboject to an uint64 NTP timestamp.
func ToNTP(t time.Time) uint64 {
// seconds since 1st January 1900
s := (float64(t.UnixNano()) / 1000000000) + 2208988800
@@ -17,14 +17,31 @@ func ToNTP(t time.Time) uint64 {
// higher 32 bits are the integer part, lower 32 bits are the fractional part
integerPart := uint32(s)
fractionalPart := uint32((s - float64(integerPart)) * 0xFFFFFFFF)
- return uint64(integerPart)<<32 | uint64(fractionalPart)
+
+ return uint64(integerPart)<<32 | uint64(fractionalPart) //nolint:gosec // G115
+}
+
+// ToNTP32 converts a time.Time object to a uint32 NTP timestamp.
+func ToNTP32(t time.Time) uint32 {
+ return uint32(ToNTP(t) >> 16) //nolint:gosec // G115
}
-// ToTime converts a uint64 NTP timestamps to a time.Time object
+// ToTime converts a uint64 NTP timestamps to a time.Time object.
func ToTime(t uint64) time.Time {
seconds := (t & 0xFFFFFFFF00000000) >> 32
fractional := float64(t&0x00000000FFFFFFFF) / float64(0xFFFFFFFF)
+ //nolint:gosec // G115
d := time.Duration(seconds)*time.Second + time.Duration(fractional*1e9)*time.Nanosecond
return time.Unix(0, 0).Add(-2208988800 * time.Second).Add(d)
}
+
+// ToTime32 converts a uint32 NTP timestamp to a time.Time object, using the
+// highest 16 bit of the reference to recover the lost bits. The low 16 bits are
+// not recovered.
+func ToTime32(t uint32, reference time.Time) time.Time {
+ referenceNTP := ToNTP(reference) & 0xFFFF000000000000
+ tu64 := ((uint64(t) << 16) & 0x0000FFFFFFFF0000) | referenceNTP
+
+ return ToTime(tu64)
+}
diff --git a/vendor/github.com/pion/interceptor/internal/rtpbuffer/errors.go b/vendor/github.com/pion/interceptor/internal/rtpbuffer/errors.go
new file mode 100644
index 0000000000..57fca02189
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/internal/rtpbuffer/errors.go
@@ -0,0 +1,15 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rtpbuffer
+
+import "errors"
+
+// ErrInvalidSize is returned by newReceiveLog/newRTPBuffer, when an incorrect buffer size is supplied.
+var ErrInvalidSize = errors.New("invalid buffer size")
+
+var (
+ errPacketReleased = errors.New("could not retain packet, already released")
+ errFailedToCastHeaderPool = errors.New("could not access header pool, failed cast")
+ errFailedToCastPayloadPool = errors.New("could not access payload pool, failed cast")
+)
diff --git a/vendor/github.com/pion/interceptor/internal/rtpbuffer/packet_factory.go b/vendor/github.com/pion/interceptor/internal/rtpbuffer/packet_factory.go
new file mode 100644
index 0000000000..3836acc394
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/internal/rtpbuffer/packet_factory.go
@@ -0,0 +1,140 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rtpbuffer
+
+import (
+ "encoding/binary"
+ "io"
+ "sync"
+
+ "github.com/pion/rtp"
+)
+
+const rtxSsrcByteLength = 2
+
+// PacketFactory allows custom logic around the handle of RTP Packets before they added to the RTPBuffer.
+// The NoOpPacketFactory doesn't copy packets, while the RetainablePacket will take a copy before adding.
+type PacketFactory interface {
+ NewPacket(header *rtp.Header, payload []byte, rtxSsrc uint32, rtxPayloadType uint8) (*RetainablePacket, error)
+}
+
+// PacketFactoryCopy is PacketFactory that takes a copy of packets when added to the RTPBuffer.
+type PacketFactoryCopy struct {
+ headerPool *sync.Pool
+ payloadPool *sync.Pool
+ rtxSequencer rtp.Sequencer
+}
+
+// NewPacketFactoryCopy constructs a PacketFactory that takes a copy of packets when added to the RTPBuffer.
+func NewPacketFactoryCopy() *PacketFactoryCopy {
+ return &PacketFactoryCopy{
+ headerPool: &sync.Pool{
+ New: func() interface{} {
+ return &rtp.Header{}
+ },
+ },
+ payloadPool: &sync.Pool{
+ New: func() interface{} {
+ buf := make([]byte, maxPayloadLen)
+
+ return &buf
+ },
+ },
+ rtxSequencer: rtp.NewRandomSequencer(),
+ }
+}
+
+// NewPacket constructs a new RetainablePacket that can be added to the RTPBuffer.
+//
+//nolint:cyclop
+func (m *PacketFactoryCopy) NewPacket(
+ header *rtp.Header, payload []byte, rtxSsrc uint32, rtxPayloadType uint8,
+) (*RetainablePacket, error) {
+ if len(payload) > maxPayloadLen {
+ return nil, io.ErrShortBuffer
+ }
+
+ retainablePacket := &RetainablePacket{
+ onRelease: m.releasePacket,
+ sequenceNumber: header.SequenceNumber,
+ // new packets have retain count of 1
+ count: 1,
+ }
+
+ var ok bool
+ retainablePacket.header, ok = m.headerPool.Get().(*rtp.Header)
+ if !ok {
+ return nil, errFailedToCastHeaderPool
+ }
+
+ *retainablePacket.header = header.Clone()
+
+ if payload != nil {
+ retainablePacket.buffer, ok = m.payloadPool.Get().(*[]byte)
+ if !ok {
+ return nil, errFailedToCastPayloadPool
+ }
+ if rtxSsrc != 0 && rtxPayloadType != 0 {
+ size := copy((*retainablePacket.buffer)[rtxSsrcByteLength:], payload)
+ retainablePacket.payload = (*retainablePacket.buffer)[:size+rtxSsrcByteLength]
+ } else {
+ size := copy(*retainablePacket.buffer, payload)
+ retainablePacket.payload = (*retainablePacket.buffer)[:size]
+ }
+ }
+
+ if rtxSsrc != 0 && rtxPayloadType != 0 {
+ if payload == nil {
+ retainablePacket.buffer, ok = m.payloadPool.Get().(*[]byte)
+ if !ok {
+ return nil, errFailedToCastPayloadPool
+ }
+ retainablePacket.payload = (*retainablePacket.buffer)[:rtxSsrcByteLength]
+ }
+ // Write the original sequence number at the beginning of the payload.
+ binary.BigEndian.PutUint16(retainablePacket.payload, retainablePacket.header.SequenceNumber)
+
+ // Rewrite the SSRC.
+ retainablePacket.header.SSRC = rtxSsrc
+ // Rewrite the payload type.
+ retainablePacket.header.PayloadType = rtxPayloadType
+ // Rewrite the sequence number.
+ retainablePacket.header.SequenceNumber = m.rtxSequencer.NextSequenceNumber()
+ // Remove padding if present.
+ if retainablePacket.header.Padding && retainablePacket.payload != nil && len(retainablePacket.payload) > 0 {
+ paddingLength := int(retainablePacket.payload[len(retainablePacket.payload)-1])
+ retainablePacket.header.Padding = false
+ retainablePacket.payload = (*retainablePacket.buffer)[:len(retainablePacket.payload)-paddingLength]
+ }
+ }
+
+ return retainablePacket, nil
+}
+
+func (m *PacketFactoryCopy) releasePacket(header *rtp.Header, payload *[]byte) {
+ m.headerPool.Put(header)
+ if payload != nil {
+ m.payloadPool.Put(payload)
+ }
+}
+
+// PacketFactoryNoOp is a PacketFactory implementation that doesn't copy packets.
+type PacketFactoryNoOp struct{}
+
+// NewPacket constructs a new RetainablePacket that can be added to the RTPBuffer.
+func (f *PacketFactoryNoOp) NewPacket(
+ header *rtp.Header, payload []byte, _ uint32, _ uint8,
+) (*RetainablePacket, error) {
+ return &RetainablePacket{
+ onRelease: f.releasePacket,
+ count: 1,
+ header: header,
+ payload: payload,
+ sequenceNumber: header.SequenceNumber,
+ }, nil
+}
+
+func (f *PacketFactoryNoOp) releasePacket(_ *rtp.Header, _ *[]byte) {
+ // no-op
+}
diff --git a/vendor/github.com/pion/interceptor/internal/rtpbuffer/retainable_packet.go b/vendor/github.com/pion/interceptor/internal/rtpbuffer/retainable_packet.go
new file mode 100644
index 0000000000..82ade097c9
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/internal/rtpbuffer/retainable_packet.go
@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rtpbuffer
+
+import (
+ "sync"
+
+ "github.com/pion/rtp"
+)
+
+// RetainablePacket is a referenced counted RTP packet.
+type RetainablePacket struct {
+ onRelease func(*rtp.Header, *[]byte)
+
+ countMu sync.Mutex
+ count int
+
+ header *rtp.Header
+ buffer *[]byte
+ payload []byte
+
+ sequenceNumber uint16
+}
+
+// Header returns the RTP Header of the RetainablePacket.
+func (p *RetainablePacket) Header() *rtp.Header {
+ return p.header
+}
+
+// Payload returns the RTP Payload of the RetainablePacket.
+func (p *RetainablePacket) Payload() []byte {
+ return p.payload
+}
+
+// Retain increases the reference count of the RetainablePacket.
+func (p *RetainablePacket) Retain() error {
+ p.countMu.Lock()
+ defer p.countMu.Unlock()
+ if p.count == 0 {
+ // already released
+ return errPacketReleased
+ }
+ p.count++
+
+ return nil
+}
+
+// Release decreases the reference count of the RetainablePacket and frees if needed.
+func (p *RetainablePacket) Release() {
+ p.countMu.Lock()
+ defer p.countMu.Unlock()
+ p.count--
+
+ if p.count == 0 {
+ // release back to pool
+ p.onRelease(p.header, p.buffer)
+ p.header = nil
+ p.buffer = nil
+ p.payload = nil
+ }
+}
diff --git a/vendor/github.com/pion/interceptor/internal/rtpbuffer/rtpbuffer.go b/vendor/github.com/pion/interceptor/internal/rtpbuffer/rtpbuffer.go
new file mode 100644
index 0000000000..94c7adfe1e
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/internal/rtpbuffer/rtpbuffer.go
@@ -0,0 +1,107 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package rtpbuffer provides a buffer for storing RTP packets
+package rtpbuffer
+
+import (
+ "fmt"
+)
+
+const (
+ // Uint16SizeHalf is half of a math.Uint16.
+ Uint16SizeHalf = 1 << 15
+
+ maxPayloadLen = 1460
+)
+
+// RTPBuffer stores RTP packets and allows custom logic
+// around the lifetime of them via the PacketFactory.
+type RTPBuffer struct {
+ packets []*RetainablePacket
+ size uint16
+ highestAdded uint16
+ started bool
+}
+
+// NewRTPBuffer constructs a new RTPBuffer.
+func NewRTPBuffer(size uint16) (*RTPBuffer, error) {
+ allowedSizes := make([]uint16, 0)
+ correctSize := false
+ for i := 0; i < 16; i++ {
+ if size == 1<= Uint16SizeHalf {
+ return nil
+ }
+
+ if diff >= r.size {
+ return nil
+ }
+
+ pkt := r.packets[seq%r.size]
+ if pkt != nil {
+ if pkt.sequenceNumber != seq {
+ return nil
+ }
+ // already released
+ if err := pkt.Retain(); err != nil {
+ return nil
+ }
+ }
+
+ return pkt
+}
diff --git a/vendor/github.com/pion/interceptor/internal/sequencenumber/unwrapper.go b/vendor/github.com/pion/interceptor/internal/sequencenumber/unwrapper.go
index 311ff09df6..48500b3cc5 100644
--- a/vendor/github.com/pion/interceptor/internal/sequencenumber/unwrapper.go
+++ b/vendor/github.com/pion/interceptor/internal/sequencenumber/unwrapper.go
@@ -9,7 +9,7 @@ const (
breakpoint = 32768 // half of max uint16
)
-// Unwrapper stores an unwrapped sequence number
+// Unwrapper stores an unwrapped sequence number.
type Unwrapper struct {
init bool
lastUnwrapped int64
@@ -19,18 +19,20 @@ func isNewer(value, previous uint16) bool {
if value-previous == breakpoint {
return value > previous
}
+
return value != previous && (value-previous) < breakpoint
}
-// Unwrap unwraps the next sequencenumber
+// Unwrap unwraps the next sequencenumber.
func (u *Unwrapper) Unwrap(i uint16) int64 {
if !u.init {
u.init = true
u.lastUnwrapped = int64(i)
+
return u.lastUnwrapped
}
- lastWrapped := uint16(u.lastUnwrapped)
+ lastWrapped := uint16(u.lastUnwrapped) //nolint:gosec // G115
delta := int64(i - lastWrapped)
if isNewer(i, lastWrapped) {
if delta < 0 {
@@ -41,5 +43,6 @@ func (u *Unwrapper) Unwrap(i uint16) int64 {
}
u.lastUnwrapped += delta
+
return u.lastUnwrapped
}
diff --git a/vendor/github.com/pion/interceptor/noop.go b/vendor/github.com/pion/interceptor/noop.go
index b0fc2a69a9..964a330e84 100644
--- a/vendor/github.com/pion/interceptor/noop.go
+++ b/vendor/github.com/pion/interceptor/noop.go
@@ -28,7 +28,8 @@ func (i *NoOp) BindLocalStream(_ *StreamInfo, writer RTPWriter) RTPWriter {
// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
func (i *NoOp) UnbindLocalStream(_ *StreamInfo) {}
-// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
+// BindRemoteStream lets you modify any incoming RTP packets.
+// It is called once for per RemoteStream. The returned method
// will be called once per rtp packet.
func (i *NoOp) BindRemoteStream(_ *StreamInfo, reader RTPReader) RTPReader {
return reader
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/errors.go b/vendor/github.com/pion/interceptor/pkg/nack/errors.go
index b47ec39c26..8b0958d015 100644
--- a/vendor/github.com/pion/interceptor/pkg/nack/errors.go
+++ b/vendor/github.com/pion/interceptor/pkg/nack/errors.go
@@ -3,13 +3,7 @@
package nack
-import "errors"
+import "github.com/pion/interceptor/internal/rtpbuffer"
-// ErrInvalidSize is returned by newReceiveLog/newSendBuffer, when an incorrect buffer size is supplied.
-var ErrInvalidSize = errors.New("invalid buffer size")
-
-var (
- errPacketReleased = errors.New("could not retain packet, already released")
- errFailedToCastHeaderPool = errors.New("could not access header pool, failed cast")
- errFailedToCastPayloadPool = errors.New("could not access payload pool, failed cast")
-)
+// ErrInvalidSize is returned by newReceiveLog/newRTPBuffer, when an incorrect buffer size is supplied.
+var ErrInvalidSize = rtpbuffer.ErrInvalidSize
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go b/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go
index ab2bb2c52a..10c8a01b48 100644
--- a/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go
+++ b/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go
@@ -13,14 +13,14 @@ import (
"github.com/pion/rtcp"
)
-// GeneratorInterceptorFactory is a interceptor.Factory for a GeneratorInterceptor
+// GeneratorInterceptorFactory is a interceptor.Factory for a GeneratorInterceptor.
type GeneratorInterceptorFactory struct {
opts []GeneratorOption
}
-// NewInterceptor constructs a new ReceiverInterceptor
+// NewInterceptor constructs a new ReceiverInterceptor.
func (g *GeneratorInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) {
- i := &GeneratorInterceptor{
+ generatorInterceptor := &GeneratorInterceptor{
streamsFilter: streamSupportNack,
size: 512,
skipLastN: 0,
@@ -33,16 +33,16 @@ func (g *GeneratorInterceptorFactory) NewInterceptor(_ string) (interceptor.Inte
}
for _, opt := range g.opts {
- if err := opt(i); err != nil {
+ if err := opt(generatorInterceptor); err != nil {
return nil, err
}
}
- if _, err := newReceiveLog(i.size); err != nil {
+ if _, err := newReceiveLog(generatorInterceptor.size); err != nil {
return nil, err
}
- return i, nil
+ return generatorInterceptor, nil
}
// GeneratorInterceptor interceptor generates nack feedback messages.
@@ -63,13 +63,13 @@ type GeneratorInterceptor struct {
receiveLogsMu sync.Mutex
}
-// NewGeneratorInterceptor returns a new GeneratorInterceptorFactory
+// NewGeneratorInterceptor returns a new GeneratorInterceptorFactory.
func NewGeneratorInterceptor(opts ...GeneratorOption) (*GeneratorInterceptorFactory, error) {
return &GeneratorInterceptorFactory{opts}, nil
}
-// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
-// will be called once per packet batch.
+// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection.
+// The returned method will be called once per packet batch.
func (n *GeneratorInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
n.m.Lock()
defer n.m.Unlock()
@@ -85,9 +85,11 @@ func (n *GeneratorInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) int
return writer
}
-// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
-// will be called once per rtp packet.
-func (n *GeneratorInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
+// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream.
+// The returned method will be called once per rtp packet.
+func (n *GeneratorInterceptor) BindRemoteStream(
+ info *interceptor.StreamInfo, reader interceptor.RTPReader,
+) interceptor.RTPReader {
if !n.streamsFilter(info) {
return reader
}
@@ -124,7 +126,7 @@ func (n *GeneratorInterceptor) UnbindRemoteStream(info *interceptor.StreamInfo)
n.receiveLogsMu.Unlock()
}
-// Close closes the interceptor
+// Close closes the interceptor.
func (n *GeneratorInterceptor) Close() error {
defer n.wg.Wait()
n.m.Lock()
@@ -137,12 +139,15 @@ func (n *GeneratorInterceptor) Close() error {
return nil
}
-// nolint:gocognit
+// nolint:gocognit,cyclop
func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
defer n.wg.Done()
senderSSRC := rand.Uint32() // #nosec
+ missingPacketSeqNums := make([]uint16, n.size)
+ filteredMissingPacket := make([]uint16, n.size)
+
ticker := time.NewTicker(n.interval)
defer ticker.Stop()
for {
@@ -153,7 +158,7 @@ func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
defer n.receiveLogsMu.Unlock()
for ssrc, receiveLog := range n.receiveLogs {
- missing := receiveLog.missingSeqNumbers(n.skipLastN)
+ missing := receiveLog.missingSeqNumbers(n.skipLastN, missingPacketSeqNums)
if len(missing) == 0 || n.nackCountLogs[ssrc] == nil {
n.nackCountLogs[ssrc] = map[uint16]uint16{}
@@ -162,22 +167,33 @@ func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
continue
}
- filteredMissing := []uint16{}
+ nack := &rtcp.TransportLayerNack{} // nolint:ineffassign,wastedassign
+
+ c := 0 // nolint:varnamelen,
if n.maxNacksPerPacket > 0 {
for _, missingSeq := range missing {
if n.nackCountLogs[ssrc][missingSeq] < n.maxNacksPerPacket {
- filteredMissing = append(filteredMissing, missingSeq)
+ filteredMissingPacket[c] = missingSeq
+ c++
}
n.nackCountLogs[ssrc][missingSeq]++
}
- } else {
- filteredMissing = missing
- }
- nack := &rtcp.TransportLayerNack{
- SenderSSRC: senderSSRC,
- MediaSSRC: ssrc,
- Nacks: rtcp.NackPairsFromSequenceNumbers(filteredMissing),
+ if c == 0 {
+ continue
+ }
+
+ nack = &rtcp.TransportLayerNack{
+ SenderSSRC: senderSSRC,
+ MediaSSRC: ssrc,
+ Nacks: rtcp.NackPairsFromSequenceNumbers(filteredMissingPacket[:c]),
+ }
+ } else {
+ nack = &rtcp.TransportLayerNack{
+ SenderSSRC: senderSSRC,
+ MediaSSRC: ssrc,
+ Nacks: rtcp.NackPairsFromSequenceNumbers(missing),
+ }
}
for nackSeq := range n.nackCountLogs[ssrc] {
@@ -185,6 +201,7 @@ func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
for _, missingSeq := range missing {
if missingSeq == nackSeq {
isMissing = true
+
break
}
}
@@ -193,10 +210,6 @@ func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
}
}
- if len(filteredMissing) == 0 {
- continue
- }
-
if _, err := rtcpWriter.Write([]rtcp.Packet{nack}, interceptor.Attributes{}); err != nil {
n.log.Warnf("failed sending nack: %+v", err)
}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go b/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go
index 5403e3eee7..db84093ae2 100644
--- a/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go
+++ b/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go
@@ -10,56 +10,63 @@ import (
"github.com/pion/logging"
)
-// GeneratorOption can be used to configure GeneratorInterceptor
+// GeneratorOption can be used to configure GeneratorInterceptor.
type GeneratorOption func(r *GeneratorInterceptor) error
// GeneratorSize sets the size of the interceptor.
-// Size must be one of: 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768
+// Size must be one of: 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768.
func GeneratorSize(size uint16) GeneratorOption {
return func(r *GeneratorInterceptor) error {
r.size = size
+
return nil
}
}
-// GeneratorSkipLastN sets the number of packets (n-1 packets before the last received packets) to ignore when generating
-// nack requests.
+// GeneratorSkipLastN sets the number of packets (n-1 packets before the last received packets)
+//
+// to ignore when generating nack requests.
func GeneratorSkipLastN(skipLastN uint16) GeneratorOption {
return func(r *GeneratorInterceptor) error {
r.skipLastN = skipLastN
+
return nil
}
}
// GeneratorMaxNacksPerPacket sets the maximum number of NACKs sent per missing packet, e.g. if set to 2, a missing
-// packet will only be NACKed at most twice. If set to 0 (default), max number of NACKs is unlimited
+// packet will only be NACKed at most twice. If set to 0 (default), max number of NACKs is unlimited.
func GeneratorMaxNacksPerPacket(maxNacks uint16) GeneratorOption {
return func(r *GeneratorInterceptor) error {
r.maxNacksPerPacket = maxNacks
+
return nil
}
}
-// GeneratorLog sets a logger for the interceptor
+// GeneratorLog sets a logger for the interceptor.
func GeneratorLog(log logging.LeveledLogger) GeneratorOption {
return func(r *GeneratorInterceptor) error {
r.log = log
+
return nil
}
}
-// GeneratorInterval sets the nack send interval for the interceptor
+// GeneratorInterval sets the nack send interval for the interceptor.
func GeneratorInterval(interval time.Duration) GeneratorOption {
return func(r *GeneratorInterceptor) error {
r.interval = interval
+
return nil
}
}
-// GeneratorStreamsFilter sets filter for generator streams
+// GeneratorStreamsFilter sets filter for generator streams.
func GeneratorStreamsFilter(filter func(info *interceptor.StreamInfo) bool) GeneratorOption {
return func(r *GeneratorInterceptor) error {
r.streamsFilter = filter
+
return nil
}
}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go b/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go
index 6a19996e76..c37407d124 100644
--- a/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go
+++ b/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go
@@ -6,6 +6,8 @@ package nack
import (
"fmt"
"sync"
+
+ "github.com/pion/interceptor/internal/rtpbuffer"
)
type receiveLog struct {
@@ -23,6 +25,7 @@ func newReceiveLog(size uint16) (*receiveLog, error) {
for i := 6; i < 16; i++ {
if size == 1< end (with counting for rollovers)
for i := s.end + 1; i != seq; i++ {
// clear packets between end and seq (these may contain packets from a "size" ago)
@@ -82,7 +86,7 @@ func (s *receiveLog) get(seq uint16) bool {
defer s.m.RUnlock()
diff := s.end - seq
- if diff >= uint16SizeHalf {
+ if diff >= rtpbuffer.Uint16SizeHalf {
return false
}
@@ -93,24 +97,25 @@ func (s *receiveLog) get(seq uint16) bool {
return s.getReceived(seq)
}
-func (s *receiveLog) missingSeqNumbers(skipLastN uint16) []uint16 {
+func (s *receiveLog) missingSeqNumbers(skipLastN uint16, missingPacketSeqNums []uint16) []uint16 {
s.m.RLock()
defer s.m.RUnlock()
until := s.end - skipLastN
- if until-s.lastConsecutive >= uint16SizeHalf {
+ if until-s.lastConsecutive >= rtpbuffer.Uint16SizeHalf {
// until < s.lastConsecutive (counting for rollover)
return nil
}
- missingPacketSeqNums := make([]uint16, 0)
+ c := 0
for i := s.lastConsecutive + 1; i != until+1; i++ {
if !s.getReceived(i) {
- missingPacketSeqNums = append(missingPacketSeqNums, i)
+ missingPacketSeqNums[c] = i
+ c++
}
}
- return missingPacketSeqNums
+ return missingPacketSeqNums[:c]
}
func (s *receiveLog) setReceived(seq uint16) {
@@ -125,6 +130,7 @@ func (s *receiveLog) delReceived(seq uint16) {
func (s *receiveLog) getReceived(seq uint16) bool {
pos := seq % s.size
+
return (s.packets[pos/64] & (1 << (pos % 64))) != 0
}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go b/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go
index 22d038ba4d..8b5585ac9b 100644
--- a/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go
+++ b/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go
@@ -7,23 +7,20 @@ import (
"sync"
"github.com/pion/interceptor"
+ "github.com/pion/interceptor/internal/rtpbuffer"
"github.com/pion/logging"
"github.com/pion/rtcp"
"github.com/pion/rtp"
)
-// ResponderInterceptorFactory is a interceptor.Factory for a ResponderInterceptor
+// ResponderInterceptorFactory is a interceptor.Factory for a ResponderInterceptor.
type ResponderInterceptorFactory struct {
opts []ResponderOption
}
-type packetFactory interface {
- NewPacket(header *rtp.Header, payload []byte, rtxSsrc uint32, rtxPayloadType uint8) (*retainablePacket, error)
-}
-
-// NewInterceptor constructs a new ResponderInterceptor
+// NewInterceptor constructs a new ResponderInterceptor.
func (r *ResponderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) {
- i := &ResponderInterceptor{
+ responderInterceptor := &ResponderInterceptor{
streamsFilter: streamSupportNack,
size: 1024,
log: logging.NewDefaultLoggerFactory().NewLogger("nack_responder"),
@@ -31,40 +28,41 @@ func (r *ResponderInterceptorFactory) NewInterceptor(_ string) (interceptor.Inte
}
for _, opt := range r.opts {
- if err := opt(i); err != nil {
+ if err := opt(responderInterceptor); err != nil {
return nil, err
}
}
- if i.packetFactory == nil {
- i.packetFactory = newPacketManager()
+ if responderInterceptor.packetFactory == nil {
+ responderInterceptor.packetFactory = rtpbuffer.NewPacketFactoryCopy()
}
- if _, err := newSendBuffer(i.size); err != nil {
+ if _, err := rtpbuffer.NewRTPBuffer(responderInterceptor.size); err != nil {
return nil, err
}
- return i, nil
+ return responderInterceptor, nil
}
-// ResponderInterceptor responds to nack feedback messages
+// ResponderInterceptor responds to nack feedback messages.
type ResponderInterceptor struct {
interceptor.NoOp
streamsFilter func(info *interceptor.StreamInfo) bool
size uint16
log logging.LeveledLogger
- packetFactory packetFactory
+ packetFactory rtpbuffer.PacketFactory
streams map[uint32]*localStream
streamsMu sync.Mutex
}
type localStream struct {
- sendBuffer *sendBuffer
- rtpWriter interceptor.RTPWriter
+ rtpBuffer *rtpbuffer.RTPBuffer
+ rtpBufferMutex sync.RWMutex
+ rtpWriter interceptor.RTPWriter
}
-// NewResponderInterceptor returns a new ResponderInterceptorFactor
+// NewResponderInterceptor returns a new ResponderInterceptorFactor.
func NewResponderInterceptor(opts ...ResponderOption) (*ResponderInterceptorFactory, error) {
return &ResponderInterceptorFactory{opts}, nil
}
@@ -98,30 +96,44 @@ func (n *ResponderInterceptor) BindRTCPReader(reader interceptor.RTCPReader) int
})
}
-// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
-// will be called once per rtp packet.
-func (n *ResponderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
+// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream.
+// The returned method will be called once per rtp packet.
+func (n *ResponderInterceptor) BindLocalStream(
+ info *interceptor.StreamInfo, writer interceptor.RTPWriter,
+) interceptor.RTPWriter {
if !n.streamsFilter(info) {
return writer
}
// error is already checked in NewGeneratorInterceptor
- sendBuffer, _ := newSendBuffer(n.size)
- n.streamsMu.Lock()
- n.streams[info.SSRC] = &localStream{
- sendBuffer: sendBuffer,
- rtpWriter: writer,
+ rtpBuffer, _ := rtpbuffer.NewRTPBuffer(n.size)
+ stream := &localStream{
+ rtpBuffer: rtpBuffer,
+ rtpWriter: writer,
}
+ n.streamsMu.Lock()
+ n.streams[info.SSRC] = stream
n.streamsMu.Unlock()
- return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
- pkt, err := n.packetFactory.NewPacket(header, payload, info.SSRCRetransmission, info.PayloadTypeRetransmission)
- if err != nil {
- return 0, err
- }
- sendBuffer.add(pkt)
- return writer.Write(header, payload, attributes)
- })
+ return interceptor.RTPWriterFunc(
+ func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
+ // If this packet doesn't belong to the main SSRC, do not add it to rtpBuffer
+ if header.SSRC != info.SSRC {
+ return writer.Write(header, payload, attributes)
+ }
+
+ pkt, err := n.packetFactory.NewPacket(header, payload, info.SSRCRetransmission, info.PayloadTypeRetransmission)
+ if err != nil {
+ return 0, err
+ }
+ stream.rtpBufferMutex.Lock()
+ defer stream.rtpBufferMutex.Unlock()
+
+ rtpBuffer.Add(pkt)
+
+ return writer.Write(header, payload, attributes)
+ },
+ )
}
// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
@@ -141,7 +153,10 @@ func (n *ResponderInterceptor) resendPackets(nack *rtcp.TransportLayerNack) {
for i := range nack.Nacks {
nack.Nacks[i].Range(func(seq uint16) bool {
- if p := stream.sendBuffer.get(seq); p != nil {
+ stream.rtpBufferMutex.Lock()
+ defer stream.rtpBufferMutex.Unlock()
+
+ if p := stream.rtpBuffer.Get(seq); p != nil {
if _, err := stream.rtpWriter.Write(p.Header(), p.Payload(), interceptor.Attributes{}); err != nil {
n.log.Warnf("failed resending nacked packet: %+v", err)
}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go b/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go
index 24c7c4693b..ea9435810f 100644
--- a/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go
+++ b/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go
@@ -5,42 +5,47 @@ package nack
import (
"github.com/pion/interceptor"
+ "github.com/pion/interceptor/internal/rtpbuffer"
"github.com/pion/logging"
)
-// ResponderOption can be used to configure ResponderInterceptor
+// ResponderOption can be used to configure ResponderInterceptor.
type ResponderOption func(s *ResponderInterceptor) error
// ResponderSize sets the size of the interceptor.
-// Size must be one of: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768
+// Size must be one of: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768.
func ResponderSize(size uint16) ResponderOption {
return func(r *ResponderInterceptor) error {
r.size = size
+
return nil
}
}
-// ResponderLog sets a logger for the interceptor
+// ResponderLog sets a logger for the interceptor.
func ResponderLog(log logging.LeveledLogger) ResponderOption {
return func(r *ResponderInterceptor) error {
r.log = log
+
return nil
}
}
// DisableCopy bypasses copy of underlying packets. It should be used when
-// you are not re-using underlying buffers of packets that have been written
+// you are not re-using underlying buffers of packets that have been written.
func DisableCopy() ResponderOption {
return func(s *ResponderInterceptor) error {
- s.packetFactory = &noOpPacketFactory{}
+ s.packetFactory = &rtpbuffer.PacketFactoryNoOp{}
+
return nil
}
}
-// ResponderStreamsFilter sets filter for local streams
+// ResponderStreamsFilter sets filter for local streams.
func ResponderStreamsFilter(filter func(info *interceptor.StreamInfo) bool) ResponderOption {
return func(r *ResponderInterceptor) error {
r.streamsFilter = filter
+
return nil
}
}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go b/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go
deleted file mode 100644
index 18c533a8a8..0000000000
--- a/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go
+++ /dev/null
@@ -1,162 +0,0 @@
-// SPDX-FileCopyrightText: 2023 The Pion community
-// SPDX-License-Identifier: MIT
-
-package nack
-
-import (
- "encoding/binary"
- "io"
- "sync"
-
- "github.com/pion/rtp"
-)
-
-const maxPayloadLen = 1460
-
-type packetManager struct {
- headerPool *sync.Pool
- payloadPool *sync.Pool
- rtxSequencer rtp.Sequencer
-}
-
-func newPacketManager() *packetManager {
- return &packetManager{
- headerPool: &sync.Pool{
- New: func() interface{} {
- return &rtp.Header{}
- },
- },
- payloadPool: &sync.Pool{
- New: func() interface{} {
- buf := make([]byte, maxPayloadLen)
- return &buf
- },
- },
- rtxSequencer: rtp.NewRandomSequencer(),
- }
-}
-
-func (m *packetManager) NewPacket(header *rtp.Header, payload []byte, rtxSsrc uint32, rtxPayloadType uint8) (*retainablePacket, error) {
- if len(payload) > maxPayloadLen {
- return nil, io.ErrShortBuffer
- }
-
- p := &retainablePacket{
- onRelease: m.releasePacket,
- sequenceNumber: header.SequenceNumber,
- // new packets have retain count of 1
- count: 1,
- }
-
- var ok bool
- p.header, ok = m.headerPool.Get().(*rtp.Header)
- if !ok {
- return nil, errFailedToCastHeaderPool
- }
-
- *p.header = header.Clone()
-
- if payload != nil {
- p.buffer, ok = m.payloadPool.Get().(*[]byte)
- if !ok {
- return nil, errFailedToCastPayloadPool
- }
-
- size := copy(*p.buffer, payload)
- p.payload = (*p.buffer)[:size]
- }
-
- if rtxSsrc != 0 && rtxPayloadType != 0 {
- // Store the original sequence number and rewrite the sequence number.
- originalSequenceNumber := p.header.SequenceNumber
- p.header.SequenceNumber = m.rtxSequencer.NextSequenceNumber()
-
- // Rewrite the SSRC.
- p.header.SSRC = rtxSsrc
- // Rewrite the payload type.
- p.header.PayloadType = rtxPayloadType
-
- // Remove padding if present.
- paddingLength := 0
- if p.header.Padding && p.payload != nil && len(p.payload) > 0 {
- paddingLength = int(p.payload[len(p.payload)-1])
- p.header.Padding = false
- }
-
- // Write the original sequence number at the beginning of the payload.
- payload := make([]byte, 2)
- binary.BigEndian.PutUint16(payload, originalSequenceNumber)
- p.payload = append(payload, p.payload[:len(p.payload)-paddingLength]...)
- }
-
- return p, nil
-}
-
-func (m *packetManager) releasePacket(header *rtp.Header, payload *[]byte) {
- m.headerPool.Put(header)
- if payload != nil {
- m.payloadPool.Put(payload)
- }
-}
-
-type noOpPacketFactory struct{}
-
-func (f *noOpPacketFactory) NewPacket(header *rtp.Header, payload []byte, _ uint32, _ uint8) (*retainablePacket, error) {
- return &retainablePacket{
- onRelease: f.releasePacket,
- count: 1,
- header: header,
- payload: payload,
- sequenceNumber: header.SequenceNumber,
- }, nil
-}
-
-func (f *noOpPacketFactory) releasePacket(_ *rtp.Header, _ *[]byte) {
- // no-op
-}
-
-type retainablePacket struct {
- onRelease func(*rtp.Header, *[]byte)
-
- countMu sync.Mutex
- count int
-
- header *rtp.Header
- buffer *[]byte
- payload []byte
-
- sequenceNumber uint16
-}
-
-func (p *retainablePacket) Header() *rtp.Header {
- return p.header
-}
-
-func (p *retainablePacket) Payload() []byte {
- return p.payload
-}
-
-func (p *retainablePacket) Retain() error {
- p.countMu.Lock()
- defer p.countMu.Unlock()
- if p.count == 0 {
- // already released
- return errPacketReleased
- }
- p.count++
- return nil
-}
-
-func (p *retainablePacket) Release() {
- p.countMu.Lock()
- defer p.countMu.Unlock()
- p.count--
-
- if p.count == 0 {
- // release back to pool
- p.onRelease(p.header, p.buffer)
- p.header = nil
- p.buffer = nil
- p.payload = nil
- }
-}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go b/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go
deleted file mode 100644
index 2b3b076f5f..0000000000
--- a/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// SPDX-FileCopyrightText: 2023 The Pion community
-// SPDX-License-Identifier: MIT
-
-package nack
-
-import (
- "fmt"
- "sync"
-)
-
-const (
- uint16SizeHalf = 1 << 15
-)
-
-type sendBuffer struct {
- packets []*retainablePacket
- size uint16
- lastAdded uint16
- started bool
-
- m sync.RWMutex
-}
-
-func newSendBuffer(size uint16) (*sendBuffer, error) {
- allowedSizes := make([]uint16, 0)
- correctSize := false
- for i := 0; i < 16; i++ {
- if size == 1<= uint16SizeHalf {
- return nil
- }
-
- if diff >= s.size {
- return nil
- }
-
- pkt := s.packets[seq%s.size]
- if pkt != nil {
- if pkt.sequenceNumber != seq {
- return nil
- }
- // already released
- if err := pkt.Retain(); err != nil {
- return nil
- }
- }
- return pkt
-}
diff --git a/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go b/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go
index 0afbd08f5b..91b513c550 100644
--- a/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go
+++ b/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go
@@ -12,14 +12,14 @@ import (
"github.com/pion/rtcp"
)
-// ReceiverInterceptorFactory is a interceptor.Factory for a ReceiverInterceptor
+// ReceiverInterceptorFactory is a interceptor.Factory for a ReceiverInterceptor.
type ReceiverInterceptorFactory struct {
opts []ReceiverOption
}
-// NewInterceptor constructs a new ReceiverInterceptor
+// NewInterceptor constructs a new ReceiverInterceptor.
func (r *ReceiverInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) {
- i := &ReceiverInterceptor{
+ receiverInterceptor := &ReceiverInterceptor{
interval: 1 * time.Second,
now: time.Now,
log: logging.NewDefaultLoggerFactory().NewLogger("receiver_interceptor"),
@@ -27,15 +27,15 @@ func (r *ReceiverInterceptorFactory) NewInterceptor(_ string) (interceptor.Inter
}
for _, opt := range r.opts {
- if err := opt(i); err != nil {
+ if err := opt(receiverInterceptor); err != nil {
return nil, err
}
}
- return i, nil
+ return receiverInterceptor, nil
}
-// NewReceiverInterceptor returns a new ReceiverInterceptorFactory
+// NewReceiverInterceptor returns a new ReceiverInterceptorFactory.
func NewReceiverInterceptor(opts ...ReceiverOption) (*ReceiverInterceptorFactory, error) {
return &ReceiverInterceptorFactory{opts}, nil
}
@@ -103,7 +103,9 @@ func (r *ReceiverInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
r.streams.Range(func(_, value interface{}) bool {
if stream, ok := value.(*receiverStream); !ok {
r.log.Warnf("failed to cast ReceiverInterceptor stream")
- } else if _, err := rtcpWriter.Write([]rtcp.Packet{stream.generateReport(now)}, interceptor.Attributes{}); err != nil {
+ } else if _, err := rtcpWriter.Write(
+ []rtcp.Packet{stream.generateReport(now)}, interceptor.Attributes{},
+ ); err != nil {
r.log.Warnf("failed sending: %+v", err)
}
@@ -116,9 +118,11 @@ func (r *ReceiverInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
}
}
-// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
-// will be called once per rtp packet.
-func (r *ReceiverInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
+// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream.
+// The returned method will be called once per rtp packet.
+func (r *ReceiverInterceptor) BindRemoteStream(
+ info *interceptor.StreamInfo, reader interceptor.RTPReader,
+) interceptor.RTPReader {
stream := newReceiverStream(info.SSRC, info.ClockRate)
r.streams.Store(info.SSRC, stream)
diff --git a/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go b/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go
index 337a341421..4a91561d9e 100644
--- a/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go
+++ b/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go
@@ -16,6 +16,7 @@ type ReceiverOption func(r *ReceiverInterceptor) error
func ReceiverLog(log logging.LeveledLogger) ReceiverOption {
return func(r *ReceiverInterceptor) error {
r.log = log
+
return nil
}
}
@@ -24,6 +25,7 @@ func ReceiverLog(log logging.LeveledLogger) ReceiverOption {
func ReceiverInterval(interval time.Duration) ReceiverOption {
return func(r *ReceiverInterceptor) error {
r.interval = interval
+
return nil
}
}
@@ -32,6 +34,7 @@ func ReceiverInterval(interval time.Duration) ReceiverOption {
func ReceiverNow(f func() time.Time) ReceiverOption {
return func(r *ReceiverInterceptor) error {
r.now = f
+
return nil
}
}
diff --git a/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go b/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go
index ebe08473d2..92913538b7 100644
--- a/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go
+++ b/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go
@@ -41,6 +41,7 @@ type receiverStream struct {
func newReceiverStream(ssrc uint32, clockRate uint32) *receiverStream {
receiverSSRC := rand.Uint32() // #nosec
+
return &receiverStream{
ssrc: ssrc,
receiverSSRC: receiverSSRC,
@@ -54,6 +55,7 @@ func (stream *receiverStream) processRTP(now time.Time, pktHeader *rtp.Header) {
stream.m.Lock()
defer stream.m.Unlock()
+ //nolint:nestif
if !stream.started { // first frame
stream.started = true
stream.setReceived(pktHeader.SequenceNumber)
@@ -104,6 +106,7 @@ func (stream *receiverStream) delReceived(seq uint16) {
func (stream *receiverStream) getReceived(seq uint16) bool {
pos := seq % (stream.size * packetsPerHistoryEntry)
+
return (stream.packets[pos/packetsPerHistoryEntry] & (1 << (pos % packetsPerHistoryEntry))) != 0
}
@@ -111,7 +114,7 @@ func (stream *receiverStream) processSenderReport(now time.Time, sr *rtcp.Sender
stream.m.Lock()
defer stream.m.Unlock()
- stream.lastSenderReport = uint32(sr.NTPTime >> 16)
+ stream.lastSenderReport = uint32(sr.NTPTime >> 16) //nolint:gosec // G115
stream.lastSenderReportTime = now
}
@@ -131,6 +134,7 @@ func (stream *receiverStream) generateReport(now time.Time) *rtcp.ReceiverReport
ret++
}
}
+
return ret
}()
stream.totalLost += totalLostSinceReport
@@ -143,7 +147,7 @@ func (stream *receiverStream) generateReport(now time.Time) *rtcp.ReceiverReport
stream.totalLost = 0xFFFFFF
}
- r := &rtcp.ReceiverReport{
+ receiverReport := &rtcp.ReceiverReport{
SSRC: stream.receiverSSRC,
Reports: []rtcp.ReceptionReport{
{
@@ -156,6 +160,7 @@ func (stream *receiverStream) generateReport(now time.Time) *rtcp.ReceiverReport
if stream.lastSenderReportTime.IsZero() {
return 0
}
+
return uint32(now.Sub(stream.lastSenderReportTime).Seconds() * 65536)
}(),
Jitter: uint32(stream.jitter),
@@ -165,5 +170,5 @@ func (stream *receiverStream) generateReport(now time.Time) *rtcp.ReceiverReport
stream.lastReportSeqnum = stream.lastSeqnum
- return r
+ return receiverReport
}
diff --git a/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go b/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go
index 1b6f4b2521..40c7f9f7a9 100644
--- a/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go
+++ b/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go
@@ -13,17 +13,17 @@ import (
"github.com/pion/rtp"
)
-// TickerFactory is a factory to create new tickers
+// TickerFactory is a factory to create new tickers.
type TickerFactory func(d time.Duration) Ticker
-// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor
+// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor.
type SenderInterceptorFactory struct {
opts []SenderOption
}
-// NewInterceptor constructs a new SenderInterceptor
+// NewInterceptor constructs a new SenderInterceptor.
func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) {
- i := &SenderInterceptor{
+ senderInterceptor := &SenderInterceptor{
interval: 1 * time.Second,
now: time.Now,
newTicker: func(d time.Duration) Ticker {
@@ -34,15 +34,15 @@ func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interce
}
for _, opt := range s.opts {
- if err := opt(i); err != nil {
+ if err := opt(senderInterceptor); err != nil {
return nil, err
}
}
- return i, nil
+ return senderInterceptor, nil
}
-// NewSenderInterceptor returns a new SenderInterceptorFactory
+// NewSenderInterceptor returns a new SenderInterceptorFactory.
func NewSenderInterceptor(opts ...SenderOption) (*SenderInterceptorFactory, error) {
return &SenderInterceptorFactory{opts}, nil
}
@@ -119,7 +119,9 @@ func (s *SenderInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
s.streams.Range(func(_, value interface{}) bool {
if stream, ok := value.(*senderStream); !ok {
s.log.Warnf("failed to cast SenderInterceptor stream")
- } else if _, err := rtcpWriter.Write([]rtcp.Packet{stream.generateReport(now)}, interceptor.Attributes{}); err != nil {
+ } else if _, err := rtcpWriter.Write(
+ []rtcp.Packet{stream.generateReport(now)}, interceptor.Attributes{},
+ ); err != nil {
s.log.Warnf("failed sending: %+v", err)
}
@@ -134,7 +136,9 @@ func (s *SenderInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
// will be called once per rtp packet.
-func (s *SenderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
+func (s *SenderInterceptor) BindLocalStream(
+ info *interceptor.StreamInfo, writer interceptor.RTPWriter,
+) interceptor.RTPWriter {
stream := newSenderStream(info.SSRC, info.ClockRate, s.useLatestPacket)
s.streams.Store(info.SSRC, stream)
diff --git a/vendor/github.com/pion/interceptor/pkg/report/sender_option.go b/vendor/github.com/pion/interceptor/pkg/report/sender_option.go
index 1e489b0b36..07a4f0875c 100644
--- a/vendor/github.com/pion/interceptor/pkg/report/sender_option.go
+++ b/vendor/github.com/pion/interceptor/pkg/report/sender_option.go
@@ -16,6 +16,7 @@ type SenderOption func(r *SenderInterceptor) error
func SenderLog(log logging.LeveledLogger) SenderOption {
return func(r *SenderInterceptor) error {
r.log = log
+
return nil
}
}
@@ -24,6 +25,7 @@ func SenderLog(log logging.LeveledLogger) SenderOption {
func SenderInterval(interval time.Duration) SenderOption {
return func(r *SenderInterceptor) error {
r.interval = interval
+
return nil
}
}
@@ -32,6 +34,7 @@ func SenderInterval(interval time.Duration) SenderOption {
func SenderNow(f func() time.Time) SenderOption {
return func(r *SenderInterceptor) error {
r.now = f
+
return nil
}
}
@@ -40,6 +43,7 @@ func SenderNow(f func() time.Time) SenderOption {
func SenderTicker(f TickerFactory) SenderOption {
return func(r *SenderInterceptor) error {
r.newTicker = f
+
return nil
}
}
@@ -49,6 +53,7 @@ func SenderTicker(f TickerFactory) SenderOption {
func SenderUseLatestPacket() SenderOption {
return func(r *SenderInterceptor) error {
r.useLatestPacket = true
+
return nil
}
}
@@ -58,6 +63,7 @@ func SenderUseLatestPacket() SenderOption {
func enableStartTracking(startedCh chan struct{}) SenderOption {
return func(r *SenderInterceptor) error {
r.started = startedCh
+
return nil
}
}
diff --git a/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go b/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go
index 44658e2464..6bb37dbff6 100644
--- a/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go
+++ b/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go
@@ -48,7 +48,7 @@ func (stream *senderStream) processRTP(now time.Time, header *rtp.Header, payloa
}
stream.packetCount++
- stream.octetCount += uint32(len(payload))
+ stream.octetCount += uint32(len(payload)) //nolint:gosec // G115
}
func (stream *senderStream) generateReport(now time.Time) *rtcp.SenderReport {
diff --git a/vendor/github.com/pion/interceptor/pkg/rfc8888/interceptor.go b/vendor/github.com/pion/interceptor/pkg/rfc8888/interceptor.go
index efe2df29c3..344ec1be17 100644
--- a/vendor/github.com/pion/interceptor/pkg/rfc8888/interceptor.go
+++ b/vendor/github.com/pion/interceptor/pkg/rfc8888/interceptor.go
@@ -14,17 +14,17 @@ import (
"github.com/pion/rtcp"
)
-// TickerFactory is a factory to create new tickers
+// TickerFactory is a factory to create new tickers.
type TickerFactory func(d time.Duration) ticker
-// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor
+// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor.
type SenderInterceptorFactory struct {
opts []Option
}
-// NewInterceptor constructs a new SenderInterceptor
+// NewInterceptor constructs a new SenderInterceptor.
func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) {
- i := &SenderInterceptor{
+ senderInterceptor := &SenderInterceptor{
NoOp: interceptor.NoOp{},
log: logging.NewDefaultLoggerFactory().NewLogger("rfc8888_interceptor"),
lock: sync.Mutex{},
@@ -40,12 +40,13 @@ func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interce
close: make(chan struct{}),
}
for _, opt := range s.opts {
- err := opt(i)
+ err := opt(senderInterceptor)
if err != nil {
return nil, err
}
}
- return i, nil
+
+ return senderInterceptor, nil
}
// NewSenderInterceptor returns a new SenderInterceptorFactory configured with the given options.
@@ -91,9 +92,12 @@ func (s *SenderInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interc
return writer
}
-// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
-// will be called once per rtp packet.
-func (s *SenderInterceptor) BindRemoteStream(_ *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
+// BindRemoteStream lets you modify any incoming RTP packets.
+// It is called once for per RemoteStream. The returned method
+// will be called once per rtp packet..
+func (s *SenderInterceptor) BindRemoteStream(
+ _ *interceptor.StreamInfo, reader interceptor.RTPReader,
+) interceptor.RTPReader {
return interceptor.RTPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
i, attr, err := reader.Read(b, a)
if err != nil {
@@ -115,6 +119,7 @@ func (s *SenderInterceptor) BindRemoteStream(_ *interceptor.StreamInfo, reader i
ecn: 0, // ECN is not supported (yet).
}
s.packetChan <- p
+
return i, attr, nil
})
}
@@ -157,16 +162,19 @@ func (s *SenderInterceptor) loop(writer interceptor.RTCPWriter) {
select {
case <-s.close:
t.Stop()
+
return
case pkt := <-s.packetChan:
s.log.Tracef("got packet: %v", pkt)
s.recorder.AddPacket(pkt.arrival, pkt.ssrc, pkt.sequenceNumber, pkt.ecn)
- case now := <-t.Ch():
+ case <-t.Ch():
+ now := s.now()
s.log.Tracef("report triggered at %v", now)
if writer == nil {
s.log.Trace("no writer added, continue")
+
continue
}
pkts := s.recorder.BuildReport(now, int(s.maxReportSize))
diff --git a/vendor/github.com/pion/interceptor/pkg/rfc8888/option.go b/vendor/github.com/pion/interceptor/pkg/rfc8888/option.go
index 1214868bf3..236073b5d2 100644
--- a/vendor/github.com/pion/interceptor/pkg/rfc8888/option.go
+++ b/vendor/github.com/pion/interceptor/pkg/rfc8888/option.go
@@ -5,13 +5,14 @@ package rfc8888
import "time"
-// An Option is a function that can be used to configure a SenderInterceptor
+// An Option is a function that can be used to configure a SenderInterceptor.
type Option func(*SenderInterceptor) error
// SenderTicker sets an alternative for time.Ticker.
func SenderTicker(f TickerFactory) Option {
return func(i *SenderInterceptor) error {
i.newTicker = f
+
return nil
}
}
@@ -20,14 +21,16 @@ func SenderTicker(f TickerFactory) Option {
func SenderNow(f func() time.Time) Option {
return func(i *SenderInterceptor) error {
i.now = f
+
return nil
}
}
-// SendInterval sets the feedback send interval for the interceptor
+// SendInterval sets the feedback send interval for the interceptor.
func SendInterval(interval time.Duration) Option {
return func(s *SenderInterceptor) error {
s.interval = interval
+
return nil
}
}
diff --git a/vendor/github.com/pion/interceptor/pkg/rfc8888/recorder.go b/vendor/github.com/pion/interceptor/pkg/rfc8888/recorder.go
index 4423df1e30..a1a2cf266e 100644
--- a/vendor/github.com/pion/interceptor/pkg/rfc8888/recorder.go
+++ b/vendor/github.com/pion/interceptor/pkg/rfc8888/recorder.go
@@ -6,6 +6,7 @@ package rfc8888
import (
"time"
+ "github.com/pion/interceptor/internal/ntp"
"github.com/pion/rtcp"
)
@@ -21,7 +22,7 @@ type Recorder struct {
streams map[uint32]*streamLog
}
-// NewRecorder creates a new Recorder
+// NewRecorder creates a new Recorder.
func NewRecorder() *Recorder {
return &Recorder{
streams: map[uint32]*streamLog{},
@@ -44,7 +45,7 @@ func (r *Recorder) BuildReport(now time.Time, maxSize int) *rtcp.CCFeedbackRepor
report := &rtcp.CCFeedbackReport{
SenderSSRC: r.ssrc,
ReportBlocks: []rtcp.CCFeedbackReportBlock{},
- ReportTimestamp: ntpTime32(now),
+ ReportTimestamp: ntp.ToNTP32(now),
}
maxReportBlocks := (maxSize - 12 - (8 * len(r.streams))) / 2
@@ -65,14 +66,3 @@ func (r *Recorder) BuildReport(now time.Time, maxSize int) *rtcp.CCFeedbackRepor
return report
}
-
-func ntpTime32(t time.Time) uint32 {
- // seconds since 1st January 1900
- s := (float64(t.UnixNano()) / 1000000000.0) + 2208988800
-
- integerPart := uint32(s)
- fractionalPart := uint32((s - float64(integerPart)) * 0xFFFFFFFF)
-
- // higher 32 bits are the integer part, lower 32 bits are the fractional part
- return uint32(((uint64(integerPart)<<32 | uint64(fractionalPart)) >> 16) & 0xFFFFFFFF)
-}
diff --git a/vendor/github.com/pion/interceptor/pkg/rfc8888/stream_log.go b/vendor/github.com/pion/interceptor/pkg/rfc8888/stream_log.go
index 8dfb14fc96..ed41f9528c 100644
--- a/vendor/github.com/pion/interceptor/pkg/rfc8888/stream_log.go
+++ b/vendor/github.com/pion/interceptor/pkg/rfc8888/stream_log.go
@@ -6,6 +6,7 @@ package rfc8888
import (
"time"
+ "github.com/pion/interceptor/internal/sequencenumber"
"github.com/pion/rtcp"
)
@@ -13,7 +14,7 @@ const maxReportsPerReportBlock = 16384
type streamLog struct {
ssrc uint32
- sequence unwrapper
+ sequence sequencenumber.Unwrapper
init bool
nextSequenceNumberToReport int64 // next to report
lastSequenceNumberReceived int64 // highest received
@@ -23,7 +24,7 @@ type streamLog struct {
func newStreamLog(ssrc uint32) *streamLog {
return &streamLog{
ssrc: ssrc,
- sequence: unwrapper{},
+ sequence: sequencenumber.Unwrapper{},
init: false,
nextSequenceNumberToReport: 0,
lastSequenceNumberReceived: 0,
@@ -32,7 +33,7 @@ func newStreamLog(ssrc uint32) *streamLog {
}
func (l *streamLog) add(ts time.Time, sequenceNumber uint16, ecn uint8) {
- unwrappedSequenceNumber := l.sequence.unwrap(sequenceNumber)
+ unwrappedSequenceNumber := l.sequence.Unwrap(sequenceNumber)
if !l.init {
l.init = true
l.nextSequenceNumberToReport = unwrappedSequenceNumber
@@ -52,7 +53,7 @@ func (l *streamLog) metricsAfter(reference time.Time, maxReportBlocks int64) rtc
if len(l.log) == 0 {
return rtcp.CCFeedbackReportBlock{
MediaSSRC: l.ssrc,
- BeginSequence: uint16(l.nextSequenceNumberToReport),
+ BeginSequence: uint16(l.nextSequenceNumberToReport), //nolint:gosec // G115
MetricBlocks: []rtcp.CCFeedbackMetricBlock{},
}
}
@@ -65,7 +66,7 @@ func (l *streamLog) metricsAfter(reference time.Time, maxReportBlocks int64) rtc
offset := l.nextSequenceNumberToReport
lastReceived := l.nextSequenceNumberToReport
gapDetected := false
- for i := offset; i <= l.lastSequenceNumberReceived; i++ {
+ for i := offset; i <= l.lastSequenceNumberReceived; i++ { //nolint:varnamelen // i int64
received := false
ecn := uint8(0)
ato := uint16(0)
@@ -91,9 +92,10 @@ func (l *streamLog) metricsAfter(reference time.Time, maxReportBlocks int64) rtc
}
}
}
+
return rtcp.CCFeedbackReportBlock{
MediaSSRC: l.ssrc,
- BeginSequence: uint16(offset),
+ BeginSequence: uint16(offset), //nolint:gosec // G115
MetricBlocks: metricBlocks,
}
}
@@ -106,5 +108,6 @@ func getArrivalTimeOffset(base time.Time, arrival time.Time) uint16 {
if ato > 0x1FFD {
return 0x1FFE
}
+
return ato
}
diff --git a/vendor/github.com/pion/interceptor/pkg/rfc8888/unwrapper.go b/vendor/github.com/pion/interceptor/pkg/rfc8888/unwrapper.go
deleted file mode 100644
index f15f33e6ef..0000000000
--- a/vendor/github.com/pion/interceptor/pkg/rfc8888/unwrapper.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// SPDX-FileCopyrightText: 2023 The Pion community
-// SPDX-License-Identifier: MIT
-
-package rfc8888
-
-const (
- maxSequenceNumberPlusOne = int64(65536)
- breakpoint = 32768 // half of max uint16
-)
-
-type unwrapper struct {
- init bool
- lastUnwrapped int64
-}
-
-func isNewer(value, previous uint16) bool {
- if value-previous == breakpoint {
- return value > previous
- }
- return value != previous && (value-previous) < breakpoint
-}
-
-func (u *unwrapper) unwrap(i uint16) int64 {
- if !u.init {
- u.init = true
- u.lastUnwrapped = int64(i)
- return u.lastUnwrapped
- }
-
- lastWrapped := uint16(u.lastUnwrapped)
- delta := int64(i - lastWrapped)
- if isNewer(i, lastWrapped) {
- if delta < 0 {
- delta += maxSequenceNumberPlusOne
- }
- } else if delta > 0 && u.lastUnwrapped+delta-maxSequenceNumberPlusOne >= 0 {
- delta -= maxSequenceNumberPlusOne
- }
-
- u.lastUnwrapped += delta
- return u.lastUnwrapped
-}
diff --git a/vendor/github.com/pion/interceptor/pkg/twcc/arrival_time_map.go b/vendor/github.com/pion/interceptor/pkg/twcc/arrival_time_map.go
index d3e2a8af36..a496ad73ce 100644
--- a/vendor/github.com/pion/interceptor/pkg/twcc/arrival_time_map.go
+++ b/vendor/github.com/pion/interceptor/pkg/twcc/arrival_time_map.go
@@ -12,6 +12,8 @@ const (
// of the arrival times of packets. It is used by the TWCC interceptor to build feedback
// packets.
// See https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/webrtc/modules/remote_bitrate_estimator/packet_arrival_map.h;drc=b5cd13bb6d5d157a5fbe3628b2dd1c1e106203c6
+//
+//nolint:lll
type packetArrivalTimeMap struct {
// arrivalTimes is a circular buffer, where the packet with sequence number sn is stored
// in slot sn % len(arrivalTimes)
@@ -31,12 +33,14 @@ func (m *packetArrivalTimeMap) AddPacket(sequenceNumber int64, arrivalTime int64
m.beginSequenceNumber = sequenceNumber
m.endSequenceNumber = sequenceNumber + 1
m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
+
return
}
if sequenceNumber >= m.beginSequenceNumber && sequenceNumber < m.endSequenceNumber {
// The packet is within the buffer, no need to resize.
m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
+
return
}
@@ -53,6 +57,7 @@ func (m *packetArrivalTimeMap) AddPacket(sequenceNumber int64, arrivalTime int64
m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
m.setNotReceived(sequenceNumber+1, m.beginSequenceNumber)
m.beginSequenceNumber = sequenceNumber
+
return
}
@@ -64,6 +69,7 @@ func (m *packetArrivalTimeMap) AddPacket(sequenceNumber int64, arrivalTime int64
m.beginSequenceNumber = sequenceNumber
m.endSequenceNumber = newEndSequenceNumber
m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
+
return
}
@@ -99,12 +105,15 @@ func (m *packetArrivalTimeMap) EndSequenceNumber() int64 {
// FindNextAtOrAfter returns the sequence number and timestamp of the first received packet that has a sequence number
// greator or equal to sequenceNumber.
-func (m *packetArrivalTimeMap) FindNextAtOrAfter(sequenceNumber int64) (foundSequenceNumber int64, arrivalTime int64, ok bool) {
+func (m *packetArrivalTimeMap) FindNextAtOrAfter(sequenceNumber int64) (
+ foundSequenceNumber int64, arrivalTime int64, ok bool,
+) {
for sequenceNumber = m.Clamp(sequenceNumber); sequenceNumber < m.endSequenceNumber; sequenceNumber++ {
if t := m.get(sequenceNumber); t >= 0 {
return sequenceNumber, t, true
}
}
+
return -1, -1, false
}
@@ -116,6 +125,7 @@ func (m *packetArrivalTimeMap) EraseTo(sequenceNumber int64) {
if sequenceNumber >= m.endSequenceNumber {
// Erase all.
m.beginSequenceNumber = m.endSequenceNumber
+
return
}
// Remove some
@@ -138,7 +148,7 @@ func (m *packetArrivalTimeMap) HasReceived(sequenceNumber int64) bool {
return m.get(sequenceNumber) >= 0
}
-// Clamp returns sequenceNumber clamped to [beginSequenceNumber, endSequenceNumber]
+// Clamp returns sequenceNumber clamped to [beginSequenceNumber, endSequenceNumber].
func (m *packetArrivalTimeMap) Clamp(sequenceNumber int64) int64 {
if sequenceNumber < m.beginSequenceNumber {
return m.beginSequenceNumber
@@ -146,6 +156,7 @@ func (m *packetArrivalTimeMap) Clamp(sequenceNumber int64) int64 {
if m.endSequenceNumber < sequenceNumber {
return m.endSequenceNumber
}
+
return sequenceNumber
}
@@ -153,6 +164,7 @@ func (m *packetArrivalTimeMap) get(sequenceNumber int64) int64 {
if sequenceNumber < m.beginSequenceNumber || sequenceNumber >= m.endSequenceNumber {
return -1
}
+
return m.arrivalTimes[m.index(sequenceNumber)]
}
diff --git a/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go b/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go
index 791b145923..52d9e84aa6 100644
--- a/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go
+++ b/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go
@@ -13,20 +13,20 @@ import (
var errHeaderIsNil = errors.New("header is nil")
-// HeaderExtensionInterceptorFactory is a interceptor.Factory for a HeaderExtensionInterceptor
+// HeaderExtensionInterceptorFactory is a interceptor.Factory for a HeaderExtensionInterceptor.
type HeaderExtensionInterceptorFactory struct{}
-// NewInterceptor constructs a new HeaderExtensionInterceptor
+// NewInterceptor constructs a new HeaderExtensionInterceptor.
func (h *HeaderExtensionInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) {
return &HeaderExtensionInterceptor{}, nil
}
-// NewHeaderExtensionInterceptor returns a HeaderExtensionInterceptorFactory
+// NewHeaderExtensionInterceptor returns a HeaderExtensionInterceptorFactory.
func NewHeaderExtensionInterceptor() (*HeaderExtensionInterceptorFactory, error) {
return &HeaderExtensionInterceptorFactory{}, nil
}
-// HeaderExtensionInterceptor adds transport wide sequence numbers as header extension to each RTP packet
+// HeaderExtensionInterceptor adds transport wide sequence numbers as header extension to each RTP packet.
type HeaderExtensionInterceptor struct {
interceptor.NoOp
nextSequenceNr uint32
@@ -36,31 +36,39 @@ const transportCCURI = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide
// BindLocalStream returns a writer that adds a rtp.TransportCCExtension
// header with increasing sequence numbers to each outgoing packet.
-func (h *HeaderExtensionInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
+func (h *HeaderExtensionInterceptor) BindLocalStream(
+ info *interceptor.StreamInfo,
+ writer interceptor.RTPWriter,
+) interceptor.RTPWriter {
var hdrExtID uint8
for _, e := range info.RTPHeaderExtensions {
if e.URI == transportCCURI {
- hdrExtID = uint8(e.ID)
+ hdrExtID = uint8(e.ID) //nolint:gosec // G115
+
break
}
}
if hdrExtID == 0 { // Don't add header extension if ID is 0, because 0 is an invalid extension ID
return writer
}
- return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
- sequenceNumber := atomic.AddUint32(&h.nextSequenceNr, 1) - 1
- tcc, err := (&rtp.TransportCCExtension{TransportSequence: uint16(sequenceNumber)}).Marshal()
- if err != nil {
- return 0, err
- }
- if header == nil {
- return 0, errHeaderIsNil
- }
- err = header.SetExtension(hdrExtID, tcc)
- if err != nil {
- return 0, err
- }
- return writer.Write(header, payload, attributes)
- })
+ return interceptor.RTPWriterFunc(
+ func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
+ sequenceNumber := atomic.AddUint32(&h.nextSequenceNr, 1) - 1
+ //nolint:gosec // G115
+ tcc, err := (&rtp.TransportCCExtension{TransportSequence: uint16(sequenceNumber)}).Marshal()
+ if err != nil {
+ return 0, err
+ }
+ if header == nil {
+ return 0, errHeaderIsNil
+ }
+ err = header.SetExtension(hdrExtID, tcc)
+ if err != nil {
+ return 0, err
+ }
+
+ return writer.Write(header, payload, attributes)
+ },
+ )
}
diff --git a/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go b/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go
index d7906fc682..229330ad44 100644
--- a/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go
+++ b/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go
@@ -14,16 +14,16 @@ import (
"github.com/pion/rtp"
)
-// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor
+// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor.
type SenderInterceptorFactory struct {
opts []Option
}
var errClosed = errors.New("interceptor is closed")
-// NewInterceptor constructs a new SenderInterceptor
+// NewInterceptor constructs a new SenderInterceptor.
func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) {
- i := &SenderInterceptor{
+ senderInterceptor := &SenderInterceptor{
log: logging.NewDefaultLoggerFactory().NewLogger("twcc_sender_interceptor"),
packetChan: make(chan packet),
close: make(chan struct{}),
@@ -32,13 +32,13 @@ func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interce
}
for _, opt := range s.opts {
- err := opt(i)
+ err := opt(senderInterceptor)
if err != nil {
return nil, err
}
}
- return i, nil
+ return senderInterceptor, nil
}
// NewSenderInterceptor returns a new SenderInterceptorFactory configured with the given options.
@@ -64,7 +64,7 @@ type SenderInterceptor struct {
packetChan chan packet
}
-// An Option is a function that can be used to configure a SenderInterceptor
+// An Option is a function that can be used to configure a SenderInterceptor.
type Option func(*SenderInterceptor) error
// SendInterval sets the interval at which the interceptor
@@ -72,6 +72,7 @@ type Option func(*SenderInterceptor) error
func SendInterval(interval time.Duration) Option {
return func(s *SenderInterceptor) error {
s.interval = interval
+
return nil
}
}
@@ -102,54 +103,63 @@ type packet struct {
ssrc uint32
}
-// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
+// BindRemoteStream lets you modify any incoming RTP packets.
+// It is called once for per RemoteStream. The returned method
// will be called once per rtp packet.
-func (s *SenderInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
+//
+//nolint:cyclop
+func (s *SenderInterceptor) BindRemoteStream(
+ info *interceptor.StreamInfo, reader interceptor.RTPReader,
+) interceptor.RTPReader {
var hdrExtID uint8
for _, e := range info.RTPHeaderExtensions {
if e.URI == transportCCURI {
- hdrExtID = uint8(e.ID)
+ hdrExtID = uint8(e.ID) //nolint:gosec // G115
+
break
}
}
if hdrExtID == 0 { // Don't try to read header extension if ID is 0, because 0 is an invalid extension ID
return reader
}
- return interceptor.RTPReaderFunc(func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
- i, attr, err := reader.Read(buf, attributes)
- if err != nil {
- return 0, nil, err
- }
- if attr == nil {
- attr = make(interceptor.Attributes)
- }
- header, err := attr.GetRTPHeader(buf[:i])
- if err != nil {
- return 0, nil, err
- }
- var tccExt rtp.TransportCCExtension
- if ext := header.GetExtension(hdrExtID); ext != nil {
- err = tccExt.Unmarshal(ext)
+ return interceptor.RTPReaderFunc(
+ func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
+ i, attr, err := reader.Read(buf, attributes)
if err != nil {
return 0, nil, err
}
- p := packet{
- hdr: header,
- sequenceNumber: tccExt.TransportSequence,
- arrivalTime: time.Since(s.startTime).Microseconds(),
- ssrc: info.SSRC,
+ if attr == nil {
+ attr = make(interceptor.Attributes)
+ }
+ header, err := attr.GetRTPHeader(buf[:i])
+ if err != nil {
+ return 0, nil, err
}
- select {
- case <-s.close:
- return 0, nil, errClosed
- case s.packetChan <- p:
+ var tccExt rtp.TransportCCExtension
+ if ext := header.GetExtension(hdrExtID); ext != nil {
+ err = tccExt.Unmarshal(ext)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ p := packet{
+ hdr: header,
+ sequenceNumber: tccExt.TransportSequence,
+ arrivalTime: time.Since(s.startTime).Microseconds(),
+ ssrc: info.SSRC,
+ }
+ select {
+ case <-s.close:
+ return 0, nil, errClosed
+ case s.packetChan <- p:
+ }
}
- }
- return i, attr, nil
- })
+ return i, attr, nil
+ },
+ )
}
// Close closes the interceptor.
@@ -174,7 +184,7 @@ func (s *SenderInterceptor) isClosed() bool {
}
}
-func (s *SenderInterceptor) loop(w interceptor.RTCPWriter) {
+func (s *SenderInterceptor) loop(writer interceptor.RTCPWriter) {
defer s.wg.Done()
select {
@@ -189,6 +199,7 @@ func (s *SenderInterceptor) loop(w interceptor.RTCPWriter) {
select {
case <-s.close:
ticker.Stop()
+
return
case p := <-s.packetChan:
s.recorder.Record(p.ssrc, p.sequenceNumber, p.arrivalTime)
@@ -199,7 +210,7 @@ func (s *SenderInterceptor) loop(w interceptor.RTCPWriter) {
if len(pkts) == 0 {
continue
}
- if _, err := w.Write(pkts, nil); err != nil {
+ if _, err := writer.Write(pkts, nil); err != nil {
s.log.Error(err.Error())
}
}
diff --git a/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go b/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go
index 6464c77bcf..750e4541d6 100644
--- a/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go
+++ b/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go
@@ -71,12 +71,13 @@ func (r *Recorder) Record(mediaSSRC uint32, sequenceNumber uint16, arrivalTime i
}
func (r *Recorder) maybeCullOldPackets(sequenceNumber int64, arrivalTime int64) {
- if r.startSequenceNumber != nil && *r.startSequenceNumber >= r.arrivalTimeMap.EndSequenceNumber() && arrivalTime >= packetWindowMicroseconds {
+ if r.startSequenceNumber != nil && *r.startSequenceNumber >= r.arrivalTimeMap.EndSequenceNumber() &&
+ arrivalTime >= packetWindowMicroseconds {
r.arrivalTimeMap.RemoveOldPackets(sequenceNumber, arrivalTime-packetWindowMicroseconds)
}
}
-// PacketsHeld returns the number of received packets currently held by the recorder
+// PacketsHeld returns the number of received packets currently held by the recorder.
func (r *Recorder) PacketsHeld() int {
return r.packetsHeld
}
@@ -101,6 +102,7 @@ func (r *Recorder) BuildFeedbackPacket() []rtcp.Packet {
// old.
}
r.packetsHeld = 0
+
return feedbacks
}
@@ -109,6 +111,7 @@ func (r *Recorder) BuildFeedbackPacket() []rtcp.Packet {
func (r *Recorder) maybeBuildFeedbackPacket(beginSeqNumInclusive, endSeqNumExclusive int64) *feedback {
// NOTE: The logic of this method is inspired by the implementation in Chrome.
// See https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.cc;l=276;drc=b5cd13bb6d5d157a5fbe3628b2dd1c1e106203c6
+ //nolint:lll
startSNInclusive, endSNExclusive := r.arrivalTimeMap.Clamp(beginSeqNumInclusive), r.arrivalTimeMap.Clamp(endSeqNumExclusive)
// Create feedback on demand, as we don't yet know if there are packets in the range that have been
@@ -136,18 +139,19 @@ func (r *Recorder) maybeBuildFeedbackPacket(beginSeqNumInclusive, endSeqNumExclu
// baseSequenceNumber is the expected first sequence number. This is known,
// but we may not have actually received it, so the base time should be the time
// of the first received packet in the feedback.
- fb.setBase(uint16(baseSequenceNumber), arrivalTime)
+ fb.setBase(uint16(baseSequenceNumber), arrivalTime) //nolint:gosec // G115
- if !fb.addReceived(uint16(seq), arrivalTime) {
+ if !fb.addReceived(uint16(seq), arrivalTime) { //nolint:gosec // G115
// Could not add a single received packet to the feedback.
// This is unexpected to actually occur, but if it does, we'll
// try again after skipping any missing packets.
// NOTE: It's fine that we already incremented fbPktCnt, as in essence
// we did actually "skip" a feedback (and this matches Chrome's behavior).
r.startSequenceNumber = &seq
+
return nil
}
- } else if !fb.addReceived(uint16(seq), arrivalTime) {
+ } else if !fb.addReceived(uint16(seq), arrivalTime) { //nolint:gosec // G115
// Could not add timestamp. Packet may be full. Return
// and try again with a fresh packet.
break
@@ -157,6 +161,7 @@ func (r *Recorder) maybeBuildFeedbackPacket(beginSeqNumInclusive, endSeqNumExclu
}
r.startSequenceNumber = &nextSequenceNumber
+
return fb
}
@@ -192,7 +197,7 @@ func (f *feedback) setBase(sequenceNumber uint16, timeUS int64) {
func (f *feedback) getRTCP() *rtcp.TransportLayerCC {
f.rtcp.PacketStatusCount = f.sequenceNumberCount
- f.rtcp.ReferenceTime = uint32(f.refTimestamp64MS)
+ f.rtcp.ReferenceTime = uint32(f.refTimestamp64MS) //nolint:gosec // G115
f.rtcp.BaseSequenceNumber = f.baseSequenceNumber
for len(f.lastChunk.deltas) > 0 {
f.chunks = append(f.chunks, f.lastChunk.encode())
@@ -200,7 +205,8 @@ func (f *feedback) getRTCP() *rtcp.TransportLayerCC {
f.rtcp.PacketChunks = append(f.rtcp.PacketChunks, f.chunks...)
f.rtcp.RecvDeltas = f.deltas
- padLen := 20 + len(f.rtcp.PacketChunks)*2 + f.len // 4 bytes header + 16 bytes twcc header + 2 bytes for each chunk + length of deltas
+ // 4 bytes header + 16 bytes twcc header + 2 bytes for each chunk + length of deltas
+ padLen := 20 + len(f.rtcp.PacketChunks)*2 + f.len
padding := padLen%4 != 0
for padLen%4 != 0 {
padLen++
@@ -209,7 +215,7 @@ func (f *feedback) getRTCP() *rtcp.TransportLayerCC {
Count: rtcp.FormatTCC,
Type: rtcp.TypeTransportSpecificFeedback,
Padding: padding,
- Length: uint16((padLen / 4) - 1),
+ Length: uint16((padLen / 4) - 1), //nolint:gosec // G115
}
return f.rtcp
@@ -223,7 +229,8 @@ func (f *feedback) addReceived(sequenceNumber uint16, timestampUS int64) bool {
} else {
delta250US = (deltaUS - rtcp.TypeTCCDeltaScaleFactor/2) / rtcp.TypeTCCDeltaScaleFactor
}
- if delta250US < math.MinInt16 || delta250US > math.MaxInt16 { // delta doesn't fit into 16 bit, need to create new packet
+ // delta doesn't fit into 16 bit, need to create new packet
+ if delta250US < math.MinInt16 || delta250US > math.MaxInt16 {
return false
}
deltaUSRounded := delta250US * rtcp.TypeTCCDeltaScaleFactor
@@ -257,6 +264,7 @@ func (f *feedback) addReceived(sequenceNumber uint16, timestampUS int64) bool {
f.lastTimestampUS += deltaUSRounded
f.sequenceNumberCount++
f.nextSequenceNumber++
+
return true
}
@@ -282,6 +290,7 @@ func (c *chunk) canAdd(delta uint16) bool {
if len(c.deltas) < maxRunLengthCap && !c.hasDifferentTypes && delta == c.deltas[0] {
return true
}
+
return false
}
@@ -294,13 +303,15 @@ func (c *chunk) add(delta uint16) {
func (c *chunk) encode() rtcp.PacketStatusChunk {
if !c.hasDifferentTypes {
defer c.reset()
+
return &rtcp.RunLengthChunk{
PacketStatusSymbol: c.deltas[0],
- RunLength: uint16(len(c.deltas)),
+ RunLength: uint16(len(c.deltas)), //nolint:gosec // G115
}
}
if len(c.deltas) == maxOneBitCap {
defer c.reset()
+
return &rtcp.StatusVectorChunk{
SymbolSize: rtcp.TypeTCCSymbolSizeOneBit,
SymbolList: c.deltas,
@@ -341,6 +352,7 @@ func maxInt(a, b int) int {
if a > b {
return a
}
+
return b
}
@@ -348,6 +360,7 @@ func minInt(a, b int) int {
if a < b {
return a
}
+
return b
}
@@ -355,6 +368,7 @@ func max64(a, b int64) int64 {
if a > b {
return a
}
+
return b
}
@@ -362,5 +376,6 @@ func min64(a, b int64) int64 {
if a < b {
return a
}
+
return b
}
diff --git a/vendor/github.com/pion/interceptor/registry.go b/vendor/github.com/pion/interceptor/registry.go
index e36ef6bfb6..9c12014267 100644
--- a/vendor/github.com/pion/interceptor/registry.go
+++ b/vendor/github.com/pion/interceptor/registry.go
@@ -13,7 +13,7 @@ func (r *Registry) Add(f Factory) {
r.factories = append(r.factories, f)
}
-// Build constructs a single Interceptor from a InterceptorRegistry
+// Build constructs a single Interceptor from a InterceptorRegistry.
func (r *Registry) Build(id string) (Interceptor, error) {
if len(r.factories) == 0 {
return &NoOp{}, nil
diff --git a/vendor/github.com/pion/interceptor/streaminfo.go b/vendor/github.com/pion/interceptor/streaminfo.go
index cb261304f8..d622312f30 100644
--- a/vendor/github.com/pion/interceptor/streaminfo.go
+++ b/vendor/github.com/pion/interceptor/streaminfo.go
@@ -9,7 +9,7 @@ type RTPHeaderExtension struct {
ID int
}
-// StreamInfo is the Context passed when a StreamLocal or StreamRemote has been Binded or Unbinded
+// StreamInfo is the Context passed when a StreamLocal or StreamRemote has been Binded or Unbinded.
type StreamInfo struct {
ID string
Attributes Attributes
diff --git a/vendor/github.com/pion/rtp/packet.go b/vendor/github.com/pion/rtp/packet.go
index c51d8c7d8e..16a3606969 100644
--- a/vendor/github.com/pion/rtp/packet.go
+++ b/vendor/github.com/pion/rtp/packet.go
@@ -29,6 +29,10 @@ type Header struct {
ExtensionProfile uint16
Extensions []Extension
+ // PaddingLength is the length of the padding in bytes. It is not part of the RTP header
+ // (it is sent in the last byte of RTP packet padding), but logically it belongs here.
+ PaddingSize byte
+
// Deprecated: will be removed in a future version.
PayloadOffset int
}
@@ -36,11 +40,16 @@ type Header struct {
// Packet represents an RTP Packet.
type Packet struct {
Header
- Payload []byte
- PaddingSize byte
+ Payload []byte
+
+ PaddingSize byte // Deprecated: will be removed in a future version. Use Header.PaddingSize instead.
// Deprecated: will be removed in a future version.
Raw []byte
+
+ // Please do not add any new field directly to Packet struct unless you know that it is safe.
+ // pion internally passes Header and Payload separately, what causes bugs like
+ // https://github.com/pion/webrtc/issues/2403 .
}
const (
@@ -219,11 +228,12 @@ func (p *Packet) Unmarshal(buf []byte) error {
if end <= n {
return errTooSmall
}
- p.PaddingSize = buf[end-1]
- end -= int(p.PaddingSize)
+ p.Header.PaddingSize = buf[end-1]
+ end -= int(p.Header.PaddingSize)
} else {
- p.PaddingSize = 0
+ p.Header.PaddingSize = 0
}
+ p.PaddingSize = p.Header.PaddingSize
if end < n {
return errTooSmall
}
@@ -490,7 +500,7 @@ func (p Packet) Marshal() (buf []byte, err error) {
// MarshalTo serializes the packet and writes to the buffer.
func (p *Packet) MarshalTo(buf []byte) (n int, err error) {
- if p.Header.Padding && p.PaddingSize == 0 {
+ if p.Header.Padding && p.paddingSize() == 0 {
return 0, errInvalidRTPPadding
}
@@ -499,23 +509,28 @@ func (p *Packet) MarshalTo(buf []byte) (n int, err error) {
return 0, err
}
+ return marshalPayloadAndPaddingTo(buf, n, &p.Header, p.Payload, p.paddingSize())
+}
+
+func marshalPayloadAndPaddingTo(buf []byte, offset int, header *Header, payload []byte, paddingSize byte,
+) (n int, err error) {
// Make sure the buffer is large enough to hold the packet.
- if n+len(p.Payload)+int(p.PaddingSize) > len(buf) {
+ if offset+len(payload)+int(paddingSize) > len(buf) {
return 0, io.ErrShortBuffer
}
- m := copy(buf[n:], p.Payload)
+ m := copy(buf[offset:], payload)
- if p.Header.Padding {
- buf[n+m+int(p.PaddingSize-1)] = p.PaddingSize
+ if header.Padding {
+ buf[offset+m+int(paddingSize-1)] = paddingSize
}
- return n + m + int(p.PaddingSize), nil
+ return offset + m + int(paddingSize), nil
}
// MarshalSize returns the size of the packet once marshaled.
func (p Packet) MarshalSize() int {
- return p.Header.MarshalSize() + len(p.Payload) + int(p.PaddingSize)
+ return p.Header.MarshalSize() + len(p.Payload) + int(p.paddingSize())
}
// Clone returns a deep copy of p.
@@ -552,3 +567,30 @@ func (h Header) Clone() Header {
return clone
}
+
+func (p *Packet) paddingSize() byte {
+ if p.Header.PaddingSize > 0 {
+ return p.Header.PaddingSize
+ }
+
+ return p.PaddingSize
+}
+
+// MarshalPacketTo serializes the header and payload into bytes.
+// Parts of pion code passes RTP header and payload separately, so this function
+// is provided to help with that.
+func MarshalPacketTo(buf []byte, header *Header, payload []byte) (int, error) {
+ n, err := header.MarshalTo(buf)
+ if err != nil {
+ return 0, err
+ }
+
+ return marshalPayloadAndPaddingTo(buf, n, header, payload, header.PaddingSize)
+}
+
+// PacketMarshalSize returns the size of the header and payload once marshaled.
+// Parts of pion code passes RTP header and payload separately, so this function
+// is provided to help with that.
+func PacketMarshalSize(header *Header, payload []byte) int {
+ return header.MarshalSize() + len(payload) + int(header.PaddingSize)
+}
diff --git a/vendor/github.com/pion/sdp/v3/util.go b/vendor/github.com/pion/sdp/v3/util.go
index 4550feece5..7cf17a9611 100644
--- a/vendor/github.com/pion/sdp/v3/util.go
+++ b/vendor/github.com/pion/sdp/v3/util.go
@@ -19,16 +19,12 @@ const (
)
var (
- errExtractCodecRtpmap = errors.New("could not extract codec from rtpmap")
- errExtractCodecFmtp = errors.New("could not extract codec from fmtp")
- errExtractCodecRtcpFb = errors.New("could not extract codec from rtcp-fb")
- errMultipleName = errors.New("codec has multiple names defined")
- errMultipleClockRate = errors.New("codec has multiple clock rates")
- errMultipleEncodingParameters = errors.New("codec has multiple encoding parameters")
- errMultipleFmtp = errors.New("codec has multiple fmtp values")
- errPayloadTypeNotFound = errors.New("payload type not found")
- errCodecNotFound = errors.New("codec not found")
- errSyntaxError = errors.New("SyntaxError")
+ errExtractCodecRtpmap = errors.New("could not extract codec from rtpmap")
+ errExtractCodecFmtp = errors.New("could not extract codec from fmtp")
+ errExtractCodecRtcpFb = errors.New("could not extract codec from rtcp-fb")
+ errPayloadTypeNotFound = errors.New("payload type not found")
+ errCodecNotFound = errors.New("codec not found")
+ errSyntaxError = errors.New("SyntaxError")
)
// ConnectionRole indicates which of the end points should initiate the connection establishment.
@@ -207,49 +203,30 @@ func parseRtcpFb(rtcpFb string) (codec Codec, isWildcard bool, err error) {
return codec, isWildcard, nil
}
-func mergeCodecs(codec Codec, codecs map[uint8]Codec) error { // nolint: cyclop
+func mergeCodecs(codec Codec, codecs map[uint8]Codec) {
savedCodec := codecs[codec.PayloadType]
- savedCodec.PayloadType = codec.PayloadType
-
- if codec.Name != "" {
- if savedCodec.Name != "" && savedCodec.Name != codec.Name {
- return errMultipleName
- }
+ if savedCodec.PayloadType == 0 {
+ savedCodec.PayloadType = codec.PayloadType
+ }
+ if savedCodec.Name == "" {
savedCodec.Name = codec.Name
}
-
- if codec.ClockRate != 0 {
- if savedCodec.ClockRate != 0 && savedCodec.ClockRate != codec.ClockRate {
- return errMultipleClockRate
- }
-
+ if savedCodec.ClockRate == 0 {
savedCodec.ClockRate = codec.ClockRate
}
-
- if codec.EncodingParameters != "" {
- if savedCodec.EncodingParameters != "" && savedCodec.EncodingParameters != codec.EncodingParameters {
- return errMultipleEncodingParameters
- }
-
+ if savedCodec.EncodingParameters == "" {
savedCodec.EncodingParameters = codec.EncodingParameters
}
-
- if codec.Fmtp != "" {
- if savedCodec.Fmtp != "" && savedCodec.Fmtp != codec.Fmtp {
- return errMultipleFmtp
- }
-
+ if savedCodec.Fmtp == "" {
savedCodec.Fmtp = codec.Fmtp
}
-
savedCodec.RTCPFeedback = append(savedCodec.RTCPFeedback, codec.RTCPFeedback...)
- codecs[savedCodec.PayloadType] = savedCodec
- return nil
+ codecs[savedCodec.PayloadType] = savedCodec
}
-func (s *SessionDescription) buildCodecMap() (map[uint8]Codec, error) { //nolint:cyclop, gocognit
+func (s *SessionDescription) buildCodecMap() map[uint8]Codec { //nolint:cyclop
codecs := map[uint8]Codec{
// static codecs that do not require a rtpmap
0: {
@@ -272,16 +249,12 @@ func (s *SessionDescription) buildCodecMap() (map[uint8]Codec, error) { //nolint
case strings.HasPrefix(attr, "rtpmap:"):
codec, err := parseRtpmap(attr)
if err == nil {
- if err = mergeCodecs(codec, codecs); err != nil {
- return nil, err
- }
+ mergeCodecs(codec, codecs)
}
case strings.HasPrefix(attr, "fmtp:"):
codec, err := parseFmtp(attr)
if err == nil {
- if err = mergeCodecs(codec, codecs); err != nil {
- return nil, err
- }
+ mergeCodecs(codec, codecs)
}
case strings.HasPrefix(attr, "rtcp-fb:"):
codec, isWildcard, err := parseRtcpFb(attr)
@@ -290,9 +263,7 @@ func (s *SessionDescription) buildCodecMap() (map[uint8]Codec, error) { //nolint
case isWildcard:
wildcardRTCPFeedback = append(wildcardRTCPFeedback, codec.RTCPFeedback...)
default:
- if err = mergeCodecs(codec, codecs); err != nil {
- return nil, err
- }
+ mergeCodecs(codec, codecs)
}
}
}
@@ -306,7 +277,7 @@ func (s *SessionDescription) buildCodecMap() (map[uint8]Codec, error) { //nolint
codecs[i] = codec
}
- return codecs, nil
+ return codecs
}
func equivalentFmtp(want, got string) bool {
@@ -350,10 +321,7 @@ func codecsMatch(wanted, got Codec) bool {
// GetCodecForPayloadType scans the SessionDescription for the given payload type and returns the codec.
func (s *SessionDescription) GetCodecForPayloadType(payloadType uint8) (Codec, error) {
- codecs, err := s.buildCodecMap()
- if err != nil {
- return Codec{}, err
- }
+ codecs := s.buildCodecMap()
codec, ok := codecs[payloadType]
if ok {
@@ -366,10 +334,7 @@ func (s *SessionDescription) GetCodecForPayloadType(payloadType uint8) (Codec, e
// GetPayloadTypeForCodec scans the SessionDescription for a codec that matches the provided codec
// as closely as possible and returns its payload type.
func (s *SessionDescription) GetPayloadTypeForCodec(wanted Codec) (uint8, error) {
- codecs, err := s.buildCodecMap()
- if err != nil {
- return 0, err
- }
+ codecs := s.buildCodecMap()
for payloadType, codec := range codecs {
if codecsMatch(wanted, codec) {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index d8a7e94136..44773832e8 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -78,8 +78,8 @@ github.com/DataDog/zstd
## explicit; go 1.14
github.com/RoaringBitmap/roaring
github.com/RoaringBitmap/roaring/internal
-# github.com/VictoriaMetrics/fastcache v1.12.2
-## explicit; go 1.13
+# github.com/VictoriaMetrics/fastcache v1.12.4
+## explicit; go 1.24.0
github.com/VictoriaMetrics/fastcache
# github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0
## explicit; go 1.18
@@ -595,7 +595,7 @@ github.com/go-llsqlite/adapter/sqlitex
github.com/go-llsqlite/crawshaw
github.com/go-llsqlite/crawshaw/c
github.com/go-llsqlite/crawshaw/sqlitex
-# github.com/go-logr/logr v1.4.2
+# github.com/go-logr/logr v1.4.3
## explicit; go 1.18
github.com/go-logr/logr
github.com/go-logr/logr/funcr
@@ -848,7 +848,7 @@ github.com/olekukonko/errors
github.com/olekukonko/ll
github.com/olekukonko/ll/lh
github.com/olekukonko/ll/lx
-# github.com/olekukonko/tablewriter v1.0.6
+# github.com/olekukonko/tablewriter v1.0.7
## explicit; go 1.21
github.com/olekukonko/tablewriter
github.com/olekukonko/tablewriter/pkg/twwarp
@@ -918,10 +918,11 @@ github.com/pion/ice/v4/internal/atomic
github.com/pion/ice/v4/internal/fakenet
github.com/pion/ice/v4/internal/stun
github.com/pion/ice/v4/internal/taskloop
-# github.com/pion/interceptor v0.1.37
+# github.com/pion/interceptor v0.1.38
## explicit; go 1.20
github.com/pion/interceptor
github.com/pion/interceptor/internal/ntp
+github.com/pion/interceptor/internal/rtpbuffer
github.com/pion/interceptor/internal/sequencenumber
github.com/pion/interceptor/pkg/nack
github.com/pion/interceptor/pkg/report
@@ -939,7 +940,7 @@ github.com/pion/randutil
# github.com/pion/rtcp v1.2.15
## explicit; go 1.20
github.com/pion/rtcp
-# github.com/pion/rtp v1.8.15
+# github.com/pion/rtp v1.8.16
## explicit; go 1.20
github.com/pion/rtp
github.com/pion/rtp/codecs
@@ -948,7 +949,7 @@ github.com/pion/rtp/codecs/vp9
# github.com/pion/sctp v1.8.39
## explicit; go 1.20
github.com/pion/sctp
-# github.com/pion/sdp/v3 v3.0.12
+# github.com/pion/sdp/v3 v3.0.13
## explicit; go 1.20
github.com/pion/sdp/v3
# github.com/pion/srtp/v3 v3.0.4
@@ -1197,7 +1198,7 @@ golang.org/x/crypto/ripemd160
golang.org/x/crypto/scrypt
golang.org/x/crypto/sha3
golang.org/x/crypto/ssh/terminal
-# golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
+# golang.org/x/exp v0.0.0-20250530174510-65e920069ea6
## explicit; go 1.23.0
golang.org/x/exp/constraints
golang.org/x/exp/rand
@@ -1357,7 +1358,7 @@ gopkg.in/yaml.v3
lukechampine.com/blake3
lukechampine.com/blake3/bao
lukechampine.com/blake3/guts
-# modernc.org/libc v1.65.7
+# modernc.org/libc v1.65.8
## explicit; go 1.23.0
modernc.org/libc
modernc.org/libc/errno
@@ -1398,7 +1399,7 @@ modernc.org/sqlite/lib
## explicit; go 1.17
rsc.io/tmplfunc
rsc.io/tmplfunc/internal/parse
-# zombiezen.com/go/sqlite v1.4.0
-## explicit; go 1.20
+# zombiezen.com/go/sqlite v1.4.2
+## explicit; go 1.23.0
zombiezen.com/go/sqlite
zombiezen.com/go/sqlite/sqlitex
diff --git a/vendor/zombiezen.com/go/sqlite/CHANGELOG.md b/vendor/zombiezen.com/go/sqlite/CHANGELOG.md
index 65a1328e01..9330d68d8d 100644
--- a/vendor/zombiezen.com/go/sqlite/CHANGELOG.md
+++ b/vendor/zombiezen.com/go/sqlite/CHANGELOG.md
@@ -5,7 +5,35 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-[Unreleased]: https://github.com/zombiezen/go-sqlite/compare/v1.4.0...main
+[Unreleased]: https://github.com/zombiezen/go-sqlite/compare/v1.4.2...main
+
+## [1.4.2][] - 2025-05-23
+
+Version 1.4.2 updates the `modernc.org/sqlite` version to 1.37.1.
+
+[1.4.2]: https://github.com/zombiezen/go-sqlite/releases/tag/v1.4.2
+
+### Changed
+
+- The minimum `modernc.org/sqlite` version updated to 1.37.1.
+
+## [1.4.1][] - 2025-05-23
+
+Version 1.4.1 updates the `modernc.org/sqlite` version to 1.36.1
+and includes a couple small improvements.
+
+[1.4.1]: https://github.com/zombiezen/go-sqlite/releases/tag/v1.4.1
+
+### Changed
+
+- The minimum `modernc.org/sqlite` version updated to 1.36.1.
+
+### Fixed
+
+- `*Stmt.ColumnName` no longer performs an allocation
+ ([#101](https://github.com/zombiezen/go-sqlite/issues/118)).
+- The doc comment for `OpenFlags` has been rewritten for clarity
+ ([#114](https://github.com/zombiezen/go-sqlite/pull/114)).
## [1.4.0][] - 2024-09-23
diff --git a/vendor/zombiezen.com/go/sqlite/flake.lock b/vendor/zombiezen.com/go/sqlite/flake.lock
index eee5b9a903..32f3f585b1 100644
--- a/vendor/zombiezen.com/go/sqlite/flake.lock
+++ b/vendor/zombiezen.com/go/sqlite/flake.lock
@@ -19,11 +19,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1701626906,
- "narHash": "sha256-ugr1QyzzwNk505ICE4VMQzonHQ9QS5W33xF2FXzFQ00=",
+ "lastModified": 1746152631,
+ "narHash": "sha256-zBuvmL6+CUsk2J8GINpyy8Hs1Zp4PP6iBWSmZ4SCQ/s=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "0c6d8c783336a59f4c59d4a6daed6ab269c4b361",
+ "rev": "032bc6539bd5f14e9d0c51bd79cfe9a055b094c3",
"type": "github"
},
"original": {
diff --git a/vendor/zombiezen.com/go/sqlite/flake.nix b/vendor/zombiezen.com/go/sqlite/flake.nix
index 5578528a28..3709ca3bc0 100644
--- a/vendor/zombiezen.com/go/sqlite/flake.nix
+++ b/vendor/zombiezen.com/go/sqlite/flake.nix
@@ -15,7 +15,7 @@
devShells.default = pkgs.mkShell {
packages = [
pkgs.go-tools # staticcheck
- pkgs.go_1_20
+ pkgs.go_1_24
pkgs.gotools # godoc, etc.
];
diff --git a/vendor/zombiezen.com/go/sqlite/go.work b/vendor/zombiezen.com/go/sqlite/go.work
index b83ccf5dd0..bc25faf0be 100644
--- a/vendor/zombiezen.com/go/sqlite/go.work
+++ b/vendor/zombiezen.com/go/sqlite/go.work
@@ -1,4 +1,6 @@
-go 1.19
+go 1.23.0
+
+toolchain go1.24.2
use (
.
diff --git a/vendor/zombiezen.com/go/sqlite/go.work.sum b/vendor/zombiezen.com/go/sqlite/go.work.sum
index 461f09aa2d..9daa7039dc 100644
--- a/vendor/zombiezen.com/go/sqlite/go.work.sum
+++ b/vendor/zombiezen.com/go/sqlite/go.work.sum
@@ -1,39 +1,58 @@
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
+github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
+github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
+golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
+golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
+golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
-golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
+lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
+modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
+modernc.org/ccgo/v3 v3.17.0/go.mod h1:Sg3fwVpmLvCUTaqEUjiBDAvshIaKDB0RXaf+zgqFu8I=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/tcl v1.15.1 h1:mOQwiEK4p7HruMZcwKTZPw/aqtGM4aY00uzWhlKKYws=
diff --git a/vendor/zombiezen.com/go/sqlite/openflags.go b/vendor/zombiezen.com/go/sqlite/openflags.go
index 7f35aad5f4..d40e74bc35 100644
--- a/vendor/zombiezen.com/go/sqlite/openflags.go
+++ b/vendor/zombiezen.com/go/sqlite/openflags.go
@@ -11,11 +11,12 @@ import (
)
// OpenFlags are [flags] used when opening a [Conn] via [OpenConn].
+// Either [OpenReadOnly] or [OpenReadWrite] must always be present.
//
// [flags]: https://www.sqlite.org/c3ref/c_open_autoproxy.html
type OpenFlags uint
-// One of the following flags must be passed to [OpenConn].
+// Required flags, one of which must be passed to [OpenConn].
const (
// OpenReadOnly opens the database in read-only mode.
// If the database does not already exist, an error is returned.
diff --git a/vendor/zombiezen.com/go/sqlite/sqlite.go b/vendor/zombiezen.com/go/sqlite/sqlite.go
index 3bacac1d77..20e73c7b58 100644
--- a/vendor/zombiezen.com/go/sqlite/sqlite.go
+++ b/vendor/zombiezen.com/go/sqlite/sqlite.go
@@ -869,7 +869,12 @@ func (stmt *Stmt) ColumnCount() int {
//
// https://sqlite.org/c3ref/column_name.html
func (stmt *Stmt) ColumnName(col int) string {
- return libc.GoString(lib.Xsqlite3_column_name(stmt.conn.tls, stmt.stmt, int32(col)))
+ for name, namedCol := range stmt.colNames {
+ if namedCol == col {
+ return name
+ }
+ }
+ return ""
}
// BindParamCount reports the number of parameters in stmt.