github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/columns/formatter/textcolumns/textcolumns.go (about)

     1  // Copyright 2022-2023 The Inspektor Gadget authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package textcolumns
    16  
    17  import (
    18  	"fmt"
    19  	"sort"
    20  	"strings"
    21  
    22  	"github.com/inspektor-gadget/inspektor-gadget/pkg/columns"
    23  )
    24  
    25  type Column[T any] struct {
    26  	col             *columns.Column[T]
    27  	calculatedWidth int
    28  	treatAsFixed    bool
    29  	formatter       func(*T) string
    30  }
    31  
    32  type TextColumnsFormatter[T any] struct {
    33  	options         *Options
    34  	columns         map[string]*Column[T]
    35  	currentMaxWidth int
    36  	showColumns     []*Column[T]
    37  	fillString      string
    38  }
    39  
    40  // NewFormatter returns a TextColumnsFormatter that will turn entries of type T into tables that can be shown
    41  // on terminals or other frontends using fixed-width characters
    42  func NewFormatter[T any](columns columns.ColumnMap[T], options ...Option) *TextColumnsFormatter[T] {
    43  	opts := DefaultOptions()
    44  	for _, o := range options {
    45  		o(opts)
    46  	}
    47  
    48  	formatterColumnMap := make(map[string]*Column[T])
    49  	for columnName, column := range columns {
    50  		formatterColumnMap[columnName] = &Column[T]{
    51  			col:             column,
    52  			calculatedWidth: column.Width,
    53  		}
    54  	}
    55  
    56  	tf := &TextColumnsFormatter[T]{
    57  		options: opts,
    58  		columns: formatterColumnMap,
    59  	}
    60  
    61  	for _, column := range tf.columns {
    62  		tf.setFormatter(column)
    63  	}
    64  
    65  	tf.SetShowColumns(opts.DefaultColumns)
    66  
    67  	return tf
    68  }
    69  
    70  // SetShowDefaultColumns resets the shown columns to those defined by default
    71  func (tf *TextColumnsFormatter[T]) SetShowDefaultColumns() {
    72  	if tf.options.DefaultColumns != nil {
    73  		tf.SetShowColumns(tf.options.DefaultColumns)
    74  		return
    75  	}
    76  	newColumns := make([]*Column[T], 0)
    77  	for _, c := range tf.columns {
    78  		if !c.col.Visible {
    79  			continue
    80  		}
    81  		newColumns = append(newColumns, c)
    82  	}
    83  
    84  	// Sort using the default sort order
    85  	sort.Slice(newColumns, func(i, j int) bool {
    86  		return newColumns[i].col.Order < newColumns[j].col.Order
    87  	})
    88  
    89  	tf.showColumns = newColumns
    90  
    91  	tf.rebuild()
    92  }
    93  
    94  // SetShowColumns takes a list of column names that will be displayed when using the output methods
    95  // Returns an error if any of the columns is not available.
    96  func (tf *TextColumnsFormatter[T]) SetShowColumns(columns []string) error {
    97  	if columns == nil {
    98  		tf.SetShowDefaultColumns()
    99  		return nil
   100  	}
   101  
   102  	newColumns := make([]*Column[T], 0)
   103  	for _, c := range columns {
   104  		column, ok := tf.columns[strings.ToLower(c)]
   105  		if !ok {
   106  			return fmt.Errorf("column %q is invalid", strings.ToLower(c))
   107  		}
   108  
   109  		newColumns = append(newColumns, column)
   110  	}
   111  	tf.showColumns = newColumns
   112  
   113  	tf.rebuild()
   114  
   115  	return nil
   116  }
   117  
   118  // SetAutoScale enables or disables the AutoScale option for the formatter. This will recalculate the widths.
   119  func (tf *TextColumnsFormatter[T]) SetAutoScale(enableAutoScale bool) {
   120  	tf.options.AutoScale = enableAutoScale
   121  	if enableAutoScale {
   122  		tf.rebuild()
   123  	} else {
   124  		// Set calculated width to configured widths
   125  		for _, column := range tf.columns {
   126  			column.calculatedWidth = column.col.Width
   127  			column.treatAsFixed = false
   128  		}
   129  		tf.buildFillString()
   130  	}
   131  }
   132  
   133  func (tf *TextColumnsFormatter[T]) rebuild() {
   134  	tf.buildFillString()
   135  	tf.currentMaxWidth = -1 // force recalculation
   136  	tf.AdjustWidthsToScreen()
   137  }