github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/columns/formatter/textcolumns/output.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  	"bytes"
    19  	"io"
    20  	"strings"
    21  
    22  	"github.com/inspektor-gadget/inspektor-gadget/pkg/columns"
    23  
    24  	"github.com/inspektor-gadget/inspektor-gadget/pkg/columns/ellipsis"
    25  )
    26  
    27  func (tf *TextColumnsFormatter[T]) setFormatter(column *Column[T]) {
    28  	ff := columns.GetFieldAsStringExt[T](column.col, 'f', column.col.Precision)
    29  	column.formatter = func(entry *T) string {
    30  		return tf.buildFixedString(ff(entry), column.calculatedWidth, column.col.EllipsisType, column.col.Alignment)
    31  	}
    32  }
    33  
    34  func (tf *TextColumnsFormatter[T]) buildFixedString(s string, length int, ellipsisType ellipsis.EllipsisType, alignment columns.Alignment) string {
    35  	if length <= 0 {
    36  		return ""
    37  	}
    38  
    39  	if !tf.options.ShouldTruncate {
    40  		return s
    41  	}
    42  
    43  	rs := []rune(s)
    44  
    45  	shortened := ellipsis.Shorten(rs, length, ellipsisType)
    46  	if len(shortened) == length {
    47  		return string(shortened)
    48  	}
    49  	if alignment == columns.AlignLeft {
    50  		return string(shortened) + tf.fillString[0:length-len(shortened)]
    51  	}
    52  	return tf.fillString[0:length-len(shortened)] + string(shortened)
    53  }
    54  
    55  // FormatEntry returns an entry as a formatted string, respecting the given formatting settings
    56  func (tf *TextColumnsFormatter[T]) FormatEntry(entry *T) string {
    57  	if entry == nil {
    58  		return ""
    59  	}
    60  
    61  	var row strings.Builder
    62  	for i, col := range tf.showColumns {
    63  		if i > 0 {
    64  			row.WriteString(tf.options.ColumnDivider)
    65  		}
    66  		row.WriteString(col.formatter(entry))
    67  	}
    68  	return row.String()
    69  }
    70  
    71  // FormatHeader returns the formatted header line with all visible column names, separated by ColumnDivider
    72  func (tf *TextColumnsFormatter[T]) FormatHeader() string {
    73  	tf.AdjustWidthsToScreen()
    74  	var row strings.Builder
    75  	for i, column := range tf.showColumns {
    76  		if i > 0 {
    77  			row.WriteString(tf.options.ColumnDivider)
    78  		}
    79  		name := column.col.Name
    80  		switch tf.options.HeaderStyle {
    81  		case HeaderStyleUppercase:
    82  			name = strings.ToUpper(name)
    83  		case HeaderStyleLowercase:
    84  			name = strings.ToLower(name)
    85  		}
    86  		row.WriteString(tf.buildFixedString(name, column.calculatedWidth, ellipsis.End, column.col.Alignment))
    87  	}
    88  	return row.String()
    89  }
    90  
    91  // FormatRowDivider returns a string that repeats the defined RowDivider until the total length of a row is reached
    92  func (tf *TextColumnsFormatter[T]) FormatRowDivider() string {
    93  	if tf.options.RowDivider == DividerNone {
    94  		return ""
    95  	}
    96  	var row strings.Builder
    97  	rowDividerLen := 0
    98  	for i, col := range tf.showColumns {
    99  		if i > 0 {
   100  			rowDividerLen += len([]rune(tf.options.ColumnDivider))
   101  		}
   102  		rowDividerLen += col.calculatedWidth
   103  	}
   104  	for i := 0; i < rowDividerLen; i += len([]rune(tf.options.RowDivider)) {
   105  		row.WriteString(tf.options.RowDivider)
   106  	}
   107  	return string([]rune(row.String())[:rowDividerLen])
   108  }
   109  
   110  // FormatTable returns header, divider and the formatted entries with the current settings as a string
   111  func (tf *TextColumnsFormatter[T]) FormatTable(entries []*T) string {
   112  	buf := bytes.NewBuffer(nil)
   113  	_ = tf.WriteTable(buf, entries)
   114  	out := buf.Bytes()
   115  	return string(out[:len(out)-1])
   116  }
   117  
   118  // WriteTable writes header, divider and the formatted entries with the current settings to writer
   119  func (tf *TextColumnsFormatter[T]) WriteTable(writer io.Writer, entries []*T) error {
   120  	_, err := writer.Write([]byte(tf.FormatHeader()))
   121  	if err != nil {
   122  		return err
   123  	}
   124  	_, err = writer.Write([]byte("\n"))
   125  	if err != nil {
   126  		return err
   127  	}
   128  	if tf.options.RowDivider != DividerNone {
   129  		_, err = writer.Write([]byte(tf.FormatRowDivider()))
   130  		if err != nil {
   131  			return err
   132  		}
   133  		_, err = writer.Write([]byte("\n"))
   134  		if err != nil {
   135  			return err
   136  		}
   137  	}
   138  	for _, entry := range entries {
   139  		_, err = writer.Write([]byte(tf.FormatEntry(entry)))
   140  		if err != nil {
   141  			return err
   142  		}
   143  		_, err = writer.Write([]byte("\n"))
   144  		if err != nil {
   145  			return err
   146  		}
   147  	}
   148  	return nil
   149  }